From 41997d49dae6aec5b29a14b6e9b850835f52d911 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Mon, 23 Feb 2026 17:13:40 +0100 Subject: [PATCH 01/31] nixos/grub: re-install grub on package change Trigger re-installation of grub files when the store path of the grub package changes. This is consistent with how side-effects are executed in other parts of NixOS, e.g. systemd service management, and ensures that the actually running grub stays in sync with the grub package in the Nix store that is part of the system closure. In particular, this causes security patches or dependency changes to be taken into effect without the need to bump the package version. Impact: This change causes a re-installation of grub for all systems, due to the lack of a persisted grub store path in existing state files. Background: So far we only re-installed grub outside of the store when its its version, the package name, or some flags changed. This leads to the various security patches only fixing new installations of grub and not existing ones. This has been like this for over a decade, but it remains unclear why the implementation decision had been to be overly cautious with modifying boot loader side effects. Fixes #486315 PL-135147 --- .../system/boot/loader/grub/install-grub.pl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl index f5ae844da21f..f61c91f02db8 100644 --- a/nixos/modules/system/boot/loader/grub/install-grub.pl +++ b/nixos/modules/system/boot/loader/grub/install-grub.pl @@ -685,12 +685,14 @@ struct(GrubState => { efi => '$', devices => '$', efiMountPoint => '$', + grub => '$', + grubEfi => '$', extraGrubInstallArgs => '@', }); # If you add something to the state file, only add it to the end # because it is read line-by-line. sub readGrubState { - my $defaultGrubState = GrubState->new(name => "", version => "", efi => "", devices => "", efiMountPoint => "", extraGrubInstallArgs => () ); + my $defaultGrubState = GrubState->new(name => "", version => "", efi => "", devices => "", efiMountPoint => "", grub => "", grubEfi => "", extraGrubInstallArgs => () ); open my $fh, "<", "$bootPath/grub/state" or return $defaultGrubState; local $/ = "\n"; my $name = <$fh>; @@ -721,8 +723,10 @@ sub readGrubState { } my %jsonState = %{decode_json($jsonStateLine)}; my @extraGrubInstallArgs = exists($jsonState{'extraGrubInstallArgs'}) ? @{$jsonState{'extraGrubInstallArgs'}} : (); + my $grubValue = exists($jsonState{'grub'}) ? $jsonState{'grub'} : ""; + my $grubEfiValue = exists($jsonState{'grubEfi'}) ? $jsonState{'grubEfi'} : ""; close $fh; - my $grubState = GrubState->new(name => $name, version => $version, efi => $efi, devices => $devices, efiMountPoint => $efiMountPoint, extraGrubInstallArgs => \@extraGrubInstallArgs ); + my $grubState = GrubState->new(name => $name, version => $version, efi => $efi, devices => $devices, efiMountPoint => $efiMountPoint, extraGrubInstallArgs => \@extraGrubInstallArgs, grub => $grubValue, grubEfi => $grubEfiValue ); return $grubState } @@ -734,15 +738,16 @@ my @prevExtraGrubInstallArgs = @{$prevGrubState->extraGrubInstallArgs}; my $devicesDiffer = scalar (List::Compare->new( '-u', '-a', \@deviceTargets, \@prevDeviceTargets)->get_symmetric_difference()); my $extraGrubInstallArgsDiffer = scalar (List::Compare->new( '-u', '-a', \@extraGrubInstallArgs, \@prevExtraGrubInstallArgs)->get_symmetric_difference()); -my $nameDiffer = get("fullName") ne $prevGrubState->name; -my $versionDiffer = get("fullVersion") ne $prevGrubState->version; my $efiDiffer = $efiTarget ne $prevGrubState->efi; my $efiMountPointDiffer = $efiSysMountPoint ne $prevGrubState->efiMountPoint; +# re-installing grub once the package store path changes is necessary, because +# introducing patches or adjusting builds does not always bump the version number +my $grubStorePathsDiffer = ($grub ne $prevGrubState->grub) || ($grubEfi ne $prevGrubState->grubEfi); if (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1") { warn "NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER"; $ENV{'NIXOS_INSTALL_BOOTLOADER'} = "1"; } -my $requireNewInstall = $devicesDiffer || $extraGrubInstallArgsDiffer || $nameDiffer || $versionDiffer || $efiDiffer || $efiMountPointDiffer || (($ENV{'NIXOS_INSTALL_BOOTLOADER'} // "") eq "1"); +my $requireNewInstall = $devicesDiffer || $extraGrubInstallArgsDiffer || $efiDiffer || $efiMountPointDiffer || $grubStorePathsDiffer || (($ENV{'NIXOS_INSTALL_BOOTLOADER'} // "") eq "1"); # install a symlink so that grub can detect the boot drive my $tmpDir = File::Temp::tempdir(CLEANUP => 1) or die "Failed to create temporary space: $!"; @@ -795,7 +800,9 @@ if ($requireNewInstall != 0) { print $fh join( ",", @deviceTargets ), "\n" or die; print $fh $efiSysMountPoint, "\n" or die; my %jsonState = ( - extraGrubInstallArgs => \@extraGrubInstallArgs + extraGrubInstallArgs => \@extraGrubInstallArgs, + grub => $grub, + grubEfi => $grubEfi ); my $jsonStateLine = encode_json(\%jsonState); print $fh $jsonStateLine, "\n" or die; From 3acd000d3477748ba18a1555eafeb65e2635d1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edward=20Tj=C3=B6rnhammar?= Date: Sat, 14 Mar 2026 17:20:34 +0100 Subject: [PATCH 02/31] slurm: add pam support --- pkgs/by-name/sl/slurm/package.nix | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/sl/slurm/package.nix b/pkgs/by-name/sl/slurm/package.nix index 974657f170e0..ea06a5f93d88 100644 --- a/pkgs/by-name/sl/slurm/package.nix +++ b/pkgs/by-name/sl/slurm/package.nix @@ -34,6 +34,7 @@ http-parser, # enable internal X11 support via libssh2 enableX11 ? true, + enablePAM ? true, enableNVML ? config.cudaSupport, cudaPackages, symlinkJoin, @@ -144,17 +145,36 @@ stdenv.mkDerivation (finalAttrs: { "--without-rpath" # Required for configure to pick up the right dlopen path ] ++ (lib.optional (!enableX11) "--disable-x11") - ++ (lib.optional enableNVML "--with-nvml"); + ++ (lib.optional enableNVML "--with-nvml") + ++ (lib.optional enablePAM "--enable-pam --with-pam_dir=${placeholder "out"}/lib/security"); preConfigure = '' patchShebangs ./doc/html/shtml2html.py patchShebangs ./doc/man/man2html.py + '' + + (lib.optionalString enablePAM '' + mkdir -p $out/lib/security + ''); + postConfigure = lib.optionalString enablePAM '' + rm -rf $out ''; - postInstall = '' - rm -f $out/lib/*.la $out/lib/slurm/*.la + postBuild = lib.optionalString enablePAM '' + make -C contribs/pam + make -C contribs/pam_slurm_adopt ''; + postInstall = + (lib.optionalString enablePAM '' + export LIBRARY_PATH="$PWD/src/api/.libs:''${LIBRARY_PATH:+:$LIBRARY_PATH}" + mkdir -p $out/lib/security + make -C contribs/pam install + make -C contribs/pam_slurm_adopt install + '') + + '' + rm -f $out/lib/*.la $out/lib/slurm/*.la $out/lib/security/*.la + ''; + enableParallelBuilding = true; passthru.tests.slurm = nixosTests.slurm; @@ -166,6 +186,7 @@ stdenv.mkDerivation (finalAttrs: { license = lib.licenses.gpl2Only; maintainers = with lib.maintainers; [ markuskowa + edwtjo ]; }; }) From 72ec3724b52991ec16aebab26ae8955c105abfc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edward=20Tj=C3=B6rnhammar?= Date: Sat, 14 Mar 2026 17:20:35 +0100 Subject: [PATCH 03/31] nixos/pam: add slurm_pam(_adopt) support --- nixos/modules/security/pam.nix | 146 +++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 9f5ad83e0746..d568b32a45a3 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -666,6 +666,137 @@ let }; }; + slurm = { + enable = lib.mkOption { + default = false; + type = lib.types.bool; + description = '' + If set, ONLY prevents users from logging into nodes if they have no + jobs in the node. This module is a legacy implementation with + functionality limited to login restrictions. + ''; + }; + + adopt = { + enable = lib.mkOption { + default = false; + type = lib.types.bool; + description = '' + If set, it prevents users from logging into nodes if they have no jobs + in the node. It also tracks any other spawned processes for accounting + and ensures complete job cleanup when a job is completed for any + successful connection. Spawned processes get "adopted" as external + steps into the current job. As such, those steps get integrated with + Slurm accounting and control group facilities. + ''; + }; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = moduleSettingsType; + options = { + + action_no_jobs = lib.mkOption { + type = lib.types.enum [ + "ignore" + "deny" + ]; + default = "deny"; + description = '' + What to do if no jobs from the user are found, deny or ignore + (pass along to next PAM module). + ''; + }; + + action_unknown = lib.mkOption { + type = lib.types.enum [ + "newest" + "allow" + "deny" + ]; + default = "newest"; + description = '' + If the user has jobs, attach them to the newest job. Allow + the connection through without adoption. + ''; + }; + + action_adopt_failure = lib.mkOption { + type = lib.types.enum [ + "allow" + "deny" + ]; + default = "deny"; + description = '' + What to do if the process is unable to be adopted into a job. + `allow` matches the upstream default which is only really + suitable for testing; production systems will want `deny` + as a default. + ''; + }; + + action_generic_failure = lib.mkOption { + type = lib.types.enum [ + "ignore" + "allow" + "deny" + ]; + default = "ignore"; + description = '' + Catch all for failures related to kernel issues or slurmd + access. Ignore falls through to the next PAM module, allowing + the connection to go through without adoption. + ''; + }; + + disable_x11 = lib.mkOption { + type = lib.types.enum [ + "0" + "1" + ]; + default = "0"; + description = '' + Disable or enable x11 sessions. '0' means the adopted connection + has Slurm X11 forwarding with DISPLAY overwritten using X11 + tunnel endpoint details. + ''; + }; + + nodename = lib.mkOption { + type = with lib.types; nullOr nonEmptyStr; + default = null; + example = "compute-a-01"; + description = '' + Set this only when the Slurm `NodeName` for this machine + differs from `hostname -s`. If unset, `pam_slurm_adopt` + uses the host short name. + ''; + }; + + join_container = lib.mkOption { + type = lib.types.enum [ + "true" + "false" + ]; + default = "true"; + description = '' + Attach to a container created by job_container/tmpfs + ''; + }; + }; + }; + + default = { + service = name; + }; + description = '' + Slurm Adopt Settings. More information is available at: + - https://slurm.schedmd.com/pam_slurm_adopt.html + ''; + }; + }; + }; + zfs = lib.mkOption { default = config.security.pam.zfs.enable; defaultText = lib.literalExpression "config.security.pam.zfs.enable"; @@ -810,6 +941,12 @@ let control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; } + { + name = "slurm"; + enable = cfg.slurm.enable; + control = "required"; + modulePath = "${pkgs.slurm}/lib/security/pam_slurm.so"; + } # The required pam_unix.so module has to come after all the sufficient modules # because otherwise, the account lookup will fail if the user does not exist # locally, for example with MySQL- or LDAP-auth. @@ -818,6 +955,15 @@ let control = "required"; modulePath = "${package}/lib/security/pam_unix.so"; } + # pam_slurm_adopt must be the last module in the account stack. + { + name = "slurm_adopt"; + enable = cfg.slurm.adopt.enable; + control = "required"; + modulePath = "${pkgs.slurm}/lib/security/pam_slurm_adopt.so"; + settings = cfg.slurm.adopt.settings; + } + ]; auth = autoOrderRules ( From 1680dd9de56adbe7c639cbf286f81b5230ddf72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edward=20Tj=C3=B6rnhammar?= Date: Sat, 14 Mar 2026 17:20:36 +0100 Subject: [PATCH 04/31] nixosTests.slurm-pam: add PAM integration testing for Slurm --- nixos/tests/all-tests.nix | 1 + nixos/tests/slurm-pam.nix | 411 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 412 insertions(+) create mode 100644 nixos/tests/slurm-pam.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 672331ab645d..fde1852208be 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1492,6 +1492,7 @@ in slimserver = runTest ./slimserver.nix; slipshow = runTest ./slipshow.nix; slurm = runTest ./slurm.nix; + slurm-pam = runTest ./slurm-pam.nix; smokeping = runTest ./smokeping.nix; snapcast = runTest ./snapcast.nix; snapper = runTest ./snapper.nix; diff --git a/nixos/tests/slurm-pam.nix b/nixos/tests/slurm-pam.nix new file mode 100644 index 000000000000..1bb613af38a0 --- /dev/null +++ b/nixos/tests/slurm-pam.nix @@ -0,0 +1,411 @@ +{ lib, pkgs, ... }: + +let + + slurmConf = "/etc/slurm.conf"; + inherit (import ./ssh-keys.nix pkgs) + snakeOilPrivateKey + snakeOilPublicKey + ; + sshConf = '' + UserKnownHostsFile /dev/null + StrictHostKeyChecking no + ''; + sshConfigPubkey = pkgs.writeText "ssh_config_pubkey" ( + sshConf + + '' + + BatchMode yes + PreferredAuthentications publickey + KbdInteractiveAuthentication no + PasswordAuthentication no + IdentityFile /root/privkey.snakeoil + '' + ); + sshConfigPassword = pkgs.writeText "ssh_config_password" ( + sshConf + + '' + BatchMode no + PreferredAuthentications password + PubkeyAuthentication no + KbdInteractiveAuthentication no + PasswordAuthentication yes + '' + ); + sshOpts = "-F " + sshConfigPubkey; + sshPassOpts = "-F " + sshConfigPassword; + adoptRemoteScript = pkgs.writeShellScript "slurm-pam-adopt-remote" '' + echo $$ > /home/submitter/ssh.pid + trap : TERM INT + while true; do + sleep 1 + done + ''; + mkWaitJob = + node: name: + pkgs.writeText "${name}.sbatch" '' + #!${pkgs.runtimeShell} + #SBATCH --job-name=${name} + #SBATCH --nodes=1 + #SBATCH --nodelist=${node} + while true; do sleep 60; done + ''; + + slurmconfig = + { config, ... }: + { + services.slurm = { + controlMachine = "control"; + nodeName = map (n: n + " CPUs=1 State=UNKNOWN") [ + "regular" + "pamslurm" + "pamslurmadopt" + ]; + partitionName = [ "debug Nodes=ALL Default=YES MaxTime=INFINITE State=UP" ]; + extraConfig = '' + PrologFlags=contain + ProctrackType=proctrack/cgroup + TaskPlugin=task/cgroup,task/affinity + SlurmdDebug=debug + ''; + }; + + services.openssh = { + enable = true; + settings = { + AllowUsers = [ "submitter" ]; + PubkeyAuthentication = true; + # leave password auth available on the regular node for + # regression testing plain pam_unix behavior + KbdInteractiveAuthentication = false; + PasswordAuthentication = true; + }; + }; + + environment.systemPackages = [ + pkgs.pamtester + pkgs.sshpass + ]; + + networking.firewall.enable = false; + systemd.tmpfiles.rules = [ + "f /etc/munge/munge.key 0400 munge munge - mungeverryweakkeybuteasytointegratoinatest" + ]; + environment.etc."slurm.conf".source = "${config.services.slurm.etcSlurm}/slurm.conf"; + systemd.services.sshd.environment.SLURM_CONF = slurmConf; + users.groups.submitter = { }; + users.users.submitter = { + isNormalUser = true; + createHome = true; + initialPassword = "submitter"; + group = "submitter"; + openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; + }; + }; + +in +{ + name = "slurm-pam"; + + meta.maintainers = [ lib.maintainers.edwtjo ]; + + nodes = + let + computeNode = + { ... }: + { + imports = [ slurmconfig ]; + services.slurm = { + client.enable = true; + }; + }; + computePAMNode = + { ... }: + { + imports = [ computeNode ]; + security.pam.services.sshd.slurm.enable = true; + services.openssh.settings = { + KbdInteractiveAuthentication = false; + PasswordAuthentication = lib.mkForce false; + PubkeyAuthentication = true; + }; + }; + computePAMAdoptNode = + { ... }: + { + imports = [ computeNode ]; + # NOTE: Prolog, Epilog needed for more advanced tests. + # This is an upstream recommended workaround for removing pam_systemd + services.slurm.extraConfig = '' + LaunchParameters=ulimit_pam_adopt + SrunProlog=${pkgs.writers.writeBash "slurm-prolog" '' + loginctl enable-linger $SLURM_JOB_USER + exit 0 + ''} + TaskProlog=${pkgs.writers.writeBash "slurm-taskprolog" '' + echo "export XDG_RUNTIME_DIR=/run/user/$SLURM_JOB_UID" + echo "export XDG_SESSION_ID=$(/tmp/wait-pamslurm.out 2>/tmp/wait-pamslurm.err &\"" + ) + submit.wait_until_succeeds( + "squeue -h -u submitter -o '%N %u %T %j' | " + "grep -Fx 'pamslurm submitter RUNNING wait-pamslurm'" + ) + submit.fail( + "ssh ${sshOpts} submitter@pamslurmadopt true", + timeout=30 + ) + submit.succeed("runuser -u submitter -- scancel -n wait-pamslurm") + submit.wait_until_succeeds( + "! squeue -h -u submitter -o '%j' | grep -Fx wait-pamslurm" + ) + + # Positive integration tests for `pam_slurm_adopt`. + # + # This subtest checks the following: + # + # 1. a job can be created specifically on `pamslurmadopt`; + # 2. Slurm reports that the job is running on that node; + # 3. the node has local Slurm state for that job (`scontrol listpids`); + # 4. SSH login is allowed while the job is active; + # 5. a long-lived SSH session is adopted into Slurm's job tracking; + # 6. cancelling the job tears down the adopted process; + # 7. SSH access is denied again once the job is gone. + # + # Validates both policy enforcement and process adoption/cleanup. + with subtest("pam_slurm_adopt_adopts_connection"): + pamslurmadopt_job = submit.succeed( + "runuser -u submitter -- sbatch --parsable ${mkWaitJob "pamslurmadopt" "wait-pamslurmadopt"}" + ).strip() + + submit.wait_until_succeeds( + f"test \"$(squeue -h -j {pamslurmadopt_job} -o %T)\" = RUNNING" + ) + submit.wait_until_succeeds( + f"squeue -h -j {pamslurmadopt_job} -o %N | grep -Fx pamslurmadopt" + ) + pamslurmadopt.wait_until_succeeds( + f"scontrol listpids | awk -v jid='{pamslurmadopt_job}' 'NR > 1 && $2 == jid {{ found = 1 }} END {{ exit !found }}'" + ) + + # Short SSH probe: verifies that login is allowed at all while the job + # is active, before we move on to the stronger adoption assertions. + submit.succeed( + "ssh ${sshOpts} submitter@pamslurmadopt true", + timeout=30 + ) + + # Start a persistent SSH session whose remote process records its PID. + # We later use that PID to prove the session was adopted into Slurm's + # accounting/control path and then cleaned up when the job is cancelled. + submit.succeed( + "sh -lc '" + "ssh ${sshOpts} submitter@pamslurmadopt ${adoptRemoteScript}" + ">/tmp/adopt-ssh.log 2>&1 & " + "echo -n \\$! > /tmp/adopt-ssh.clientpid'" + ) + + # Wait until the remote helper has started and published its PID. + pamslurmadopt.wait_until_succeeds("test -s /home/submitter/ssh.pid") + + # Prove that the SSH-spawned remote process is visible through Slurm's + # local process listing, i.e. that the session was adopted into the job. + pamslurmadopt.wait_until_succeeds( + "pid=$(cat /home/submitter/ssh.pid); " + "scontrol listpids | awk 'NR > 1 { print $1 }' | grep -Fx \"$pid\"" + ) + remote_pid = pamslurmadopt.succeed("cat /home/submitter/ssh.pid").strip() + + # Cancel the allocation and verify the entire chain is torn down: + # the Slurm job disappears, the adopted remote process exits, and the + # local SSH client exits as well. + submit.succeed(f"runuser -u submitter -- scancel {pamslurmadopt_job}") + submit.wait_until_succeeds( + f"test -z \"$(squeue -h -j {pamslurmadopt_job})\"", + timeout=60, + ) + pamslurmadopt.wait_until_succeeds( + f"! test -e /proc/{remote_pid}", + timeout=60, + ) + submit.wait_until_succeeds( + "! kill -0 $(cat /tmp/adopt-ssh.clientpid)", + timeout=60, + ) + + # Once the job is gone, SSH must be denied + # again on the adopt-protected node. + submit.fail( + "ssh ${sshOpts} submitter@pamslurmadopt true", + timeout=30 + ) + ''; +} From 80d9b9cc39ab85e505aa7ebc8f0c9a85633d1b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edward=20Tj=C3=B6rnhammar?= Date: Sat, 14 Mar 2026 17:20:37 +0100 Subject: [PATCH 05/31] nixosTests.slurm: test cluster and job state --- nixos/tests/slurm.nix | 80 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/nixos/tests/slurm.nix b/nixos/tests/slurm.nix index b468e32e45e0..06b622739852 100644 --- a/nixos/tests/slurm.nix +++ b/nixos/tests/slurm.nix @@ -148,6 +148,8 @@ in }; testScript = '' + start_all() + with subtest("can_start_slurmdbd"): dbd.wait_for_unit("slurmdbd.service") dbd.wait_for_open_port(6819) @@ -155,36 +157,82 @@ in with subtest("cluster_is_initialized"): control.wait_for_unit("multi-user.target") control.wait_for_unit("slurmctld.service") - control.wait_until_succeeds("sacctmgr list cluster | awk '{ print $1 }' | grep default") + control.wait_for_open_port(6817) - start_all() - - with subtest("can_start_slurmd"): for node in [node1, node2, node3]: - node.wait_for_unit("slurmd") + node.wait_for_unit("slurmd.service") + node.wait_for_open_port(6818) - # Test that the cluster works and can distribute jobs; - submit.wait_for_unit("multi-user.target") + submit.wait_for_unit("multi-user.target") + + control.wait_until_succeeds( + "sacctmgr -nP list cluster format=cluster | grep -qx default" + ) + + # Test that the cluster works and can distribute jobs; + control.wait_until_succeeds( + "sinfo -Nh -o '%N %T' | grep -Fx 'node1 idle' && " + "sinfo -Nh -o '%N %T' | grep -Fx 'node2 idle' && " + "sinfo -Nh -o '%N %T' | grep -Fx 'node3 idle'" + ) with subtest("run_distributed_command"): # Run `hostname` on 3 nodes of the partition (so on all the 3 nodes). # The output must contain the 3 different names - submit.succeed("srun -N 3 hostname | sort | uniq | wc -l | xargs test 3 -eq") + submit.succeed( + "test \"$(srun -J distributed-hostname-check -N 3 hostname | sort -u | tr '\n' ' ')\" = 'node1 node2 node3 '" + ) - with subtest("check_slurm_dbd_job"): - # find the srun job from above in the database - control.wait_until_succeeds("sacct | grep hostname") + with subtest("check_slurm_dbd_job_for_srun"): + # find the srun job from above in the database + submit.wait_until_succeeds( + "sacct -X -P -n --name=distributed-hostname-check -o JobName,State | " + "grep -Eq '^distributed-hostname-check\\|COMPLETED(\\+.*)?$'" + ) with subtest("run_PMIx_mpitest"): - submit.succeed("srun -N 3 --mpi=pmix mpitest | grep size=3") + submit.succeed( + "out=$(srun -N 3 --mpi=pmix mpitest); " + "echo \"$out\"; " + "echo \"$out\" | grep -Fx 'size=3'; " + "test \"$(echo \"$out\" | grep -c 'hello world from process')\" -eq 3" + ) with subtest("run_sbatch"): - submit.succeed("sbatch --wait ${sbatchScript}") - submit.succeed("grep 'sbatch success' ${sbatchOutput}") + submit.succeed( + "jobid=$(sbatch --parsable --wait ${sbatchScript}); " + "echo \"$jobid\" > /tmp/sbatch.jobid" + ) + submit.succeed("grep -Fx 'sbatch success' ${sbatchOutput}") + submit.wait_until_succeeds( + "sacct -X -j $(cat /tmp/sbatch.jobid) -n -o State | grep -Eq 'COMPLETED|COMPLETED\\+'" + ) + submit.succeed("test -z \"$(squeue -h)\"") + + with subtest("cluster_returns_to_idle"): + control.wait_until_succeeds( + "sinfo -Nh -o '%N %T' | grep -Fx 'node1 idle' && " + "sinfo -Nh -o '%N %T' | grep -Fx 'node2 idle' && " + "sinfo -Nh -o '%N %T' | grep -Fx 'node3 idle'" + ) with subtest("rest"): rest.wait_for_unit("slurmrestd.service") - token = control.succeed("scontrol token").split('=')[1].rstrip() - rest.succeed("${pkgs.curl}/bin/curl -sk -H X-SLURM-USER-TOKEN:%s -X GET 'http://localhost:6820/slurm/v0.0.43/diag'" % token) + rest.wait_for_open_port(6820) + + token = control.succeed("scontrol token").split('=', 1)[1].strip() + + rest.succeed( + "${pkgs.curl}/bin/curl -fsS " + "-H X-SLURM-USER-TOKEN:%s " + "http://localhost:6820/slurm/v0.0.43/diag | grep -q 'meta'" % token + ) + + with subtest("rest_rejects_invalid_token"): + rest.fail( + "${pkgs.curl}/bin/curl -fsS " + "-H X-SLURM-USER-TOKEN:not-a-real-token " + "http://localhost:6820/slurm/v0.0.43/diag" + ) ''; } From 355908755a54d1f0c7311284185fcc5b60fb5f68 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Mon, 5 Jan 2026 09:57:43 +0100 Subject: [PATCH 06/31] nixos/network-interfaces: remove network-setup This change solves some big design flaws in the scripted networking backend. 1. When the module was reworked in 072c1dcc4a31 to use the new systemd targets, network-setup.service was used to: a. order the services and link network.target to the boot sequence b. perform some perform final network configuration, specifically setting the default gateways and nameservers (/etc/resolv.conf). Later (ec00b4bb1186) however, network-setup.service was made optional: if resolvconf is not used and a default gateway is not set, the service is not be defined (because it would result in an empty script). Doing so, however, has the unintended effect of unlinking network.target from multi-uset.target, meaning no network configuration at all is performed. Note: this can be easily seen by adding `networking.resolvconf.enable = false` to the nixosTests.networking.scripted.static test. 2. The network-addresses-*.service are linked to network.target, which, in turn, is linked to multi-uset.target. This means that if a hardware interface is not found, the boot will hang until this service times out (issue #154737). To solve issue 1. this change removes network-target entirely while - moving the default gateway setup into the relative network-addresses-*.service unit; - moving the nameservers setup into networking.localCommands; (incidentally, this also fixes issue #445496) - directly linking network.target to multi-user.target. To solve issue 2. this removes the Wants=network.target dependency of network-addresses-*.service and solely relies on the underlying interface unit (*-netdev.service for virtual, *.device for physical) to start the service. Note: for NixOS containers, the dependency is kept, because the .device unit are not available in this case. Finally, if an interface is the default gateway, network-online.target is added as an extra dependency, so the target is not reached until the interface has been plugged in and configured. --- .../tasks/network-interfaces-scripted.nix | 290 +++++++++--------- nixos/modules/tasks/network-interfaces.nix | 21 +- .../networking/networkd-and-scripted.nix | 47 ++- 3 files changed, 212 insertions(+), 146 deletions(-) diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index 4b457e078e77..fe6f6e5a1cb8 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -51,6 +51,71 @@ let (lib.concatStringsSep " ") ]; + # Converts an IPv4 address literal to a list of bits + parseAddr.ipv4 = + addr: + let + pad = b: lib.replicate (8 - builtins.length b) 0 ++ b; + toBin = n: pad (lib.toBaseDigits 2 (lib.toInt n)); + in + lib.concatMap toBin (builtins.splitVersion addr); + + # Converts an IPv6 address literal to a list of bits + parseAddr.ipv6 = + addr: + let + pad = b: lib.replicate (16 - builtins.length b) 0 ++ b; + fromHex = n: (builtins.fromTOML "n = 0x${n}").n; + toBin = n: pad (lib.toBaseDigits 2 (fromHex n)); + normal = (lib.network.ipv6.fromString addr).address; + in + lib.concatMap toBin (lib.splitString ":" normal); + + # Checks if `addr` is part of the `net` subnet + inSubnet = + v: net: addr: + let + prefix = lib.take net.prefixLength (parseAddr.${v} net.address); + match = lib.zipListsWith (a: b: a == b) prefix (parseAddr.${v} addr); + in + lib.all lib.id match; + + # Checks if the netmask of all addresses on interface `iface` includes + # the IP address of `gateway` + # + # Note: this is used to check whether networking.defaultGateway relies on + # the given interface, either explicitly, via the `interface` (optional), + # or explicitly, by using an address in a subnet of this interface. + # + # Configuration of the default gateway is then performed as part of that + # interface setup in `configureAddrs`, below. + isGateway = + v: gateway: iface: + lib.any lib.id ( + [ (iface.name == gateway.interface) ] + ++ map (net: inSubnet v net gateway.address) iface.${v}.addresses + ); + + # Checks if `gateway` uses an address from `iface` as default source + # + # Note: this is needed to delay the configuration of the gateway and default + # source until the right interfaces and address have been set up, otherwise + # the commands will fail. + hasSource = + v: gateway: iface: + builtins.elem gateway.source (map (i: i.address) iface.${v}.addresses); + + # Interfaces corresponding to the default gateways + gateway4Iface = builtins.filter (isGateway "ipv4" cfg.defaultGateway) interfaces; + gateway6Iface = builtins.filter (isGateway "ipv6" cfg.defaultGateway6) interfaces; + + # Interfaces corresponding to the default source addresses + # + # Note: the use of `head` here is safe because these expressions + # are evaluated only when `needsSourceIface`, see `configureAddrs` below. + source4Iface = builtins.head (builtins.filter (hasSource "ipv4" cfg.defaultGateway) interfaces); + source6Iface = builtins.head (builtins.filter (hasSource "ipv6" cfg.defaultGateway6) interfaces); + # warn that these attributes are deprecated (2017-2-2) # Should be removed in the release after next bondDeprecation = rec { @@ -118,121 +183,70 @@ let else optional (!config.boot.isContainer) (subsystemDevice dev); - hasDefaultGatewaySet = - (cfg.defaultGateway != null && cfg.defaultGateway.address != "") - || (cfg.enableIPv6 && cfg.defaultGateway6 != null && cfg.defaultGateway6.address != ""); - - needNetworkSetup = - cfg.resolvconf.enable || cfg.defaultGateway != null || cfg.defaultGateway6 != null; - - networkLocalCommands = lib.mkIf needNetworkSetup { - after = [ "network-setup.service" ]; - bindsTo = [ "network-setup.service" ]; - }; - - networkSetup = lib.mkIf needNetworkSetup { - description = "Networking Setup"; - - after = [ "network-pre.target" ]; - before = [ - "network.target" - "shutdown.target" - ]; - wants = [ "network.target" ]; - # exclude bridges from the partOf relationship to fix container networking bug #47210 - partOf = map (i: "network-addresses-${i.name}.service") ( - filter (i: !(hasAttr i.name cfg.bridges)) interfaces - ); - conflicts = [ "shutdown.target" ]; - wantedBy = [ "multi-user.target" ] ++ optional hasDefaultGatewaySet "network-online.target"; - - unitConfig.ConditionCapability = "CAP_NET_ADMIN"; - - path = [ pkgs.iproute2 ]; - - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - - unitConfig.DefaultDependencies = false; - - script = '' - ${optionalString config.networking.resolvconf.enable '' - # Set the static DNS configuration, if given. - ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <, create a job ‘network-addresses-.service" - # that performs static address configuration. It has a "wants" - # dependency on ‘.service’, which is supposed to create - # the interface and need not exist (i.e. for hardware - # interfaces). It has a binds-to dependency on the actual - # network device, so it only gets started after the interface - # has appeared, and it's stopped when the interface - # disappears. + # For each interface , creates a network-addresses-.service + # job that performs static address configuration. + # + # It has a Wants dependency on -netdev.service, which creates + # create the interface, or on a device unit (for hardware interfaces). + # It also has a BindsTo dependency on the device unit: so, it only gets + # started after the interface has appeared and it's stopped when the + # interface disappears. + # + # Unless in a container, the job is not made part of network.target, so + # if an interface is not found (e.g. a USB interface not plugged in) it + # will not hang the boot sequence. + # + # If the interface is the default gateway, the job will also set the + # default gateway and delay network-online.target. configureAddrs = i: let ips = interfaceIps i; + isDefaultGateway4 = cfg.defaultGateway != null && builtins.elem i gateway4Iface; + isDefaultGateway6 = cfg.defaultGateway6 != null && builtins.elem i gateway6Iface; + needsSourceIface4 = + isDefaultGateway4 && cfg.defaultGateway.source != null && i.name != source4Iface.name; + needsSourceIface6 = + isDefaultGateway6 && cfg.defaultGateway6.source != null && i.name != source6Iface.name; + + configureGateway = + version: gateway: + optionalString (gateway.address != "") '' + echo -n "setting ${i.name} as default IPv${version} gateway... " + ${optionalString (gateway.interface != null) '' + ip -${version} route replace ${gateway.address} proto static ${ + formatIpArgs { + metric = gateway.metric; + dev = gateway.interface; + } + } + ''} + ip -${version} route replace default proto static ${ + formatIpArgs { + metric = gateway.metric; + via = gateway.address; + window = cfg.defaultGatewayWindowSize; + dev = gateway.interface; + src = gateway.source; + } + } + echo "done" + ''; in nameValuePair "network-addresses-${i.name}" { description = "Address configuration of ${i.name}"; - wantedBy = [ - "network-setup.service" - "network.target" - ]; - # order before network-setup because the routes that are configured - # there may need ip addresses configured - before = [ "network-setup.service" ]; + + wantedBy = + deviceDependency i.name + ++ optional config.boot.isContainer "network.target" + ++ optional (isDefaultGateway4 || isDefaultGateway6) "network-online.target"; bindsTo = deviceDependency i.name; - after = [ "network-pre.target" ] ++ (deviceDependency i.name); + after = [ + "network-pre.target" + ] + ++ optional needsSourceIface4 "network-addresses-${source4Iface.name}.service" + ++ optional needsSourceIface6 "network-addresses-${source6Iface.name}.service" + ++ deviceDependency i.name; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; # Restart rather than stop+start this unit to prevent the @@ -284,6 +298,10 @@ let fi '' )} + + # Set the default gateway + ${optionalString isDefaultGateway4 (configureGateway "4" cfg.defaultGateway)} + ${optionalString isDefaultGateway6 (configureGateway "6" cfg.defaultGateway6)} ''; preStop = '' state="/run/nixos/network/routes/${i.name}" @@ -314,10 +332,9 @@ let after = optional (!config.boot.isContainer) "dev-net-tun.device" ++ [ "network-pre.target" ]; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice i.name) ]; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; path = [ pkgs.iproute2 ]; serviceConfig = { Type = "oneshot"; @@ -343,18 +360,18 @@ let description = "Bridge Interface ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ]; bindsTo = deps ++ optional v.rstp "mstpd.service"; - partOf = [ "network-setup.service" ] ++ optional v.rstp "mstpd.service"; + partOf = [ "network.target" ] + ++ optional v.rstp "mstpd.service"; after = [ "network-pre.target" ] ++ deps ++ optional v.rstp "mstpd.service" ++ map (i: "network-addresses-${i}.service") v.interfaces; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -448,15 +465,11 @@ let description = "Open vSwitch Interface ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ] ++ internalConfigs; - # before = [ "network-setup.service" ]; - # should work without internalConfigs dependencies because address/link configuration depends - # on the device, which is created by ovs-vswitchd with type=internal, but it does not... - before = [ "network-setup.service" ] ++ internalConfigs; - partOf = [ "network-setup.service" ]; # shutdown the bridge when network is shutdown + before = [ "network.target" ] ++ internalConfigs; + partOf = [ "network.target" ]; # shutdown the bridge when network is shutdown bindsTo = [ "ovs-vswitchd.service" ]; # requires ovs-vswitchd to be alive at all times after = [ "network-pre.target" @@ -521,12 +534,11 @@ let description = "Bond Interface ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; after = [ "network-pre.target" ] ++ deps ++ map (i: "network-addresses-${i}.service") v.interfaces; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ @@ -570,12 +582,11 @@ let description = "MACVLAN Interface ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; after = [ "network-pre.target" ] ++ deps; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -602,12 +613,12 @@ let description = "IPVLAN Interface ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; + partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -647,12 +658,11 @@ let description = "FOU endpoint ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; after = [ "network-pre.target" ] ++ deps; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -677,12 +687,11 @@ let description = "IPv6 in IPv4 Tunnel Interface ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; after = [ "network-pre.target" ] ++ deps; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -720,12 +729,11 @@ let description = "IP in IP Tunnel Interface ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; after = [ "network-pre.target" ] ++ deps; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -768,12 +776,11 @@ let description = "GRE Tunnel Interface ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; after = [ "network-pre.target" ] ++ deps; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -803,13 +810,12 @@ let description = "VLAN Interface ${n}"; wantedBy = [ "network.target" - "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "network-setup.service" ]; + partOf = [ "network.target" ]; after = [ "network-pre.target" ] ++ deps; - before = [ "network-setup.service" ]; + before = [ "network.target" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -845,10 +851,20 @@ let // mapAttrs' createGreDevice cfg.greTunnels // mapAttrs' createVlanDevice cfg.vlans // { - network-setup = networkSetup; - network-local-commands = networkLocalCommands; + network-local-commands = { + after = [ "network-pre.target" ]; + wantedBy = [ "network.target" ]; + }; }; + # Note: the scripted networking backend consistent of many + # independent services that are linked to the network.target. + # Since there is no daemon (e.g systemd-networkd) that is + # started as part of the system and pulls in network.target. + # Thus, to start these services we link network.target directly + # to multi-user.target, this has the same result. + systemd.targets.network.wantedBy = [ "multi-user.target" ]; + services.udev.extraRules = '' KERNEL=="tun", TAG+="systemd" ''; diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index 83d60ba80e40..d0fe0ed8443f 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -747,10 +747,9 @@ in default = ""; example = "text=anything; echo You can put $text here."; description = '' - Shell commands to be executed at the end of the - `network-setup` systemd service. Note that if - you are using DHCP to obtain the network configuration, - interfaces may not be fully configured yet. + Shell commands to be executed after all the network + interfaces have been created, but not necessarily + fully configured. ''; }; @@ -1851,6 +1850,20 @@ in ''; }; }; + + networking.localCommands = lib.mkIf config.networking.resolvconf.enable '' + # Set the static DNS configuration, if given. + ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static < Date: Wed, 21 Jan 2026 00:49:00 +0100 Subject: [PATCH 07/31] nixos/networking-interfaces: add networking-scripted.target This adds a target that groups together all the services of the scripted backend. It has no role during the boot, but provides a quick way to reset the network configuration. For example, if addresses/routes are added/removed or an interface has been stopped manually, running systemctl restart networking-scripted.target will undo all the changes. --- .../tasks/network-interfaces-scripted.nix | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index fe6f6e5a1cb8..4bf274d4157f 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -241,6 +241,7 @@ let ++ optional config.boot.isContainer "network.target" ++ optional (isDefaultGateway4 || isDefaultGateway6) "network-online.target"; bindsTo = deviceDependency i.name; + partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] @@ -329,6 +330,7 @@ let nameValuePair "${i.name}-netdev" { description = "Virtual Network Interface ${i.name}"; bindsTo = optional (!config.boot.isContainer) "dev-net-tun.device"; + partOf = [ "networking-scripted.target" ]; after = optional (!config.boot.isContainer) "dev-net-tun.device" ++ [ "network-pre.target" ]; wantedBy = [ "network.target" @@ -363,7 +365,10 @@ let (subsystemDevice n) ]; bindsTo = deps ++ optional v.rstp "mstpd.service"; - partOf = [ "network.target" ] + partOf = [ + "network.target" + "networking-scripted.target" + ] ++ optional v.rstp "mstpd.service"; after = [ "network-pre.target" @@ -469,7 +474,10 @@ let ] ++ internalConfigs; before = [ "network.target" ] ++ internalConfigs; - partOf = [ "network.target" ]; # shutdown the bridge when network is shutdown + partOf = [ + "network.target" + "networking-scripted.target" + ]; # shutdown the bridge when network is shutdown bindsTo = [ "ovs-vswitchd.service" ]; # requires ovs-vswitchd to be alive at all times after = [ "network-pre.target" @@ -537,6 +545,7 @@ let (subsystemDevice n) ]; bindsTo = deps; + partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps ++ map (i: "network-addresses-${i}.service") v.interfaces; before = [ "network.target" ]; serviceConfig.Type = "oneshot"; @@ -585,6 +594,7 @@ let (subsystemDevice n) ]; bindsTo = deps; + partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; before = [ "network.target" ]; serviceConfig.Type = "oneshot"; @@ -661,6 +671,7 @@ let (subsystemDevice n) ]; bindsTo = deps; + partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; before = [ "network.target" ]; serviceConfig.Type = "oneshot"; @@ -732,6 +743,7 @@ let (subsystemDevice n) ]; bindsTo = deps; + partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; before = [ "network.target" ]; serviceConfig.Type = "oneshot"; @@ -779,6 +791,7 @@ let (subsystemDevice n) ]; bindsTo = deps; + partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; before = [ "network.target" ]; serviceConfig.Type = "oneshot"; @@ -813,7 +826,10 @@ let (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "network.target" ]; + partOf = [ + "network.target" + "networking-scripted.target" + ]; after = [ "network-pre.target" ] ++ deps; before = [ "network.target" ]; serviceConfig.Type = "oneshot"; @@ -865,6 +881,13 @@ let # to multi-user.target, this has the same result. systemd.targets.network.wantedBy = [ "multi-user.target" ]; + # This target serves no purpose during the boot, but can be + # used to quickly reset the network configuration by running + # systemctl restart networking-scripted.target + systemd.targets.networking-scripted = { + description = "NixOS scripted networking setup"; + }; + services.udev.extraRules = '' KERNEL=="tun", TAG+="systemd" ''; From 8bc8c596ead9703f76af1f65a62f3e31560b0f1e Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Tue, 6 Jan 2026 00:47:45 +0100 Subject: [PATCH 08/31] nixos/doc: update networking chapter --- .../ad-hoc-network-config.section.md | 20 ++++++++++++++----- .../configuration/ipv4-config.section.md | 13 +++++++++--- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/nixos/doc/manual/configuration/ad-hoc-network-config.section.md b/nixos/doc/manual/configuration/ad-hoc-network-config.section.md index 7ef24825526d..611f3e3956ee 100644 --- a/nixos/doc/manual/configuration/ad-hoc-network-config.section.md +++ b/nixos/doc/manual/configuration/ad-hoc-network-config.section.md @@ -1,14 +1,24 @@ # Ad-Hoc Configuration {#ad-hoc-network-config} -You can use [](#opt-networking.localCommands) to -specify shell commands to be run at the end of `network-setup.service`. This -is useful for doing network configuration not covered by the existing NixOS -modules. For instance, to statically configure an IPv6 address: +You can use [](#opt-networking.localCommands) to specify shell commands to be +run after the network interfaces have been created, but not necessarily fully +configured. +This is useful for doing network configuration not covered by the existing +NixOS modules. For example, you can create a network namespace and a pair +of virtual ethernet devices like this: ```nix { networking.localCommands = '' - ip -6 addr add 2001:610:685:1::1/64 dev eth0 + ip netns add mynet + ip link add name veth-in type veth peer name veth-out + ip link set dev veth-out netns mynet ''; } ``` + +::: {.note} +The commands should ideally be idempotent, so it's recommended to perform +cleanups of the state you create (e.g. virtual interfaces), or at least make +sure possible failures are handled. +::: diff --git a/nixos/doc/manual/configuration/ipv4-config.section.md b/nixos/doc/manual/configuration/ipv4-config.section.md index ed0db9da8c3e..ceb9a350b680 100644 --- a/nixos/doc/manual/configuration/ipv4-config.section.md +++ b/nixos/doc/manual/configuration/ipv4-config.section.md @@ -26,9 +26,16 @@ servers: ``` ::: {.note} -Statically configured interfaces are set up by the systemd service -`interface-name-cfg.service`. The default gateway and name server -configuration is performed by `network-setup.service`. +Addresses and routes for statically configured interfaces and the default +gateway are set up by systemd services named +`network-addresses-.service`. The name servers configuration, +instead, is performed by `network-local-commands.service` using resolvconf. +::: + +::: {.note} +If needed, for example if addresses/routes were added/removed, +you can reset the network configuration by running +`systemctl restart networking-scripted.target` ::: The host name is set using [](#opt-networking.hostName): From 1623fe233169fc86917ee79b23710eaa6695d109 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Sat, 14 Mar 2026 14:50:00 +0100 Subject: [PATCH 09/31] nixos/release-notes: explain network-interfaces changes --- nixos/doc/manual/release-notes/rl-2605.section.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index b6df37e26327..a98528159278 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -178,6 +178,13 @@ See . Note for NetworkManager users: before these changes NetworkManager used to spawn its own wpa_supplicant daemon, but now it relies on `networking.wireless`. So, if you had `networking.wireless.enable = false` in your configuration, you should remove that line. +- Some implementation details of the NixOS network-interfaces module have been changed: + + - In the "scripted" backend, `network-setup.service` has been removed and the network configuration services are now part of `network.target`, which is now directly pulled into `multi-user.target`. + - Interface addresses, routes and default gateways are now configured asynchronously as soon as the underlying network devices become available (fixes issue [#154737](https://github.com/NixOS/nixpkgs/issues/154737)). + - In both "networkd" and "scripted" backends, the configuration of name servers is now part of `network-local-commands.service` (fixes issue [#445496](https://github.com/NixOS/nixpkgs/issues/445496)). + - The issue that resulted in a completely unconfigured network if both `resolvconf` was disabled and no default gateway configured, has also been fixed. + - `kratos` has been updated from 1.3.1 to [25.4.0](https://github.com/ory/kratos/releases/tag/v25.4.0). Upstream switched to a new versioning scheme (year.major.minor). Notable breaking changes: - The `migrate sql` CLI command is now `migrate sql up` From 6ffb2e7e2015d681d4457cda0a267121ab5ce2b9 Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Sun, 31 Aug 2025 15:09:05 +0000 Subject: [PATCH 10/31] libiscsi: 1.20.0 -> 1.20.3 --- pkgs/by-name/li/libiscsi/package.nix | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkgs/by-name/li/libiscsi/package.nix b/pkgs/by-name/li/libiscsi/package.nix index 7b8f5d74aea0..b6d93ee53bb2 100644 --- a/pkgs/by-name/li/libiscsi/package.nix +++ b/pkgs/by-name/li/libiscsi/package.nix @@ -7,13 +7,13 @@ stdenv.mkDerivation (finalAttrs: { pname = "libiscsi"; - version = "1.20.0"; + version = "1.20.3"; src = fetchFromGitHub { owner = "sahlberg"; repo = "libiscsi"; rev = finalAttrs.version; - sha256 = "sha256-idiK9JowKhGAk5F5qJ57X14Q2Y0TbIKRI02onzLPkas="; + sha256 = "sha256-ARajWZ5/LIfFNCdp3HvQiyhR455+sJNzUPbBrz/pZ7E="; }; postPatch = '' @@ -24,8 +24,14 @@ stdenv.mkDerivation (finalAttrs: { nativeBuildInputs = [ autoreconfHook ]; env = lib.optionalAttrs (stdenv.hostPlatform.is32bit || stdenv.hostPlatform.isDarwin) { - # iscsi-discard.c:223:57: error: format specifies type 'unsigned long' but the argument has type 'uint64_t' (aka 'unsigned long long') [-Werror,-Wformat] - NIX_CFLAGS_COMPILE = "-Wno-error=format"; + NIX_CFLAGS_COMPILE = toString [ + # iscsi-discard.c:223:57: error: format specifies type 'unsigned long' but the argument has type 'uint64_t' (aka 'unsigned long long') [-Werror,-Wformat] + "-Wno-error=format" + # multithreading.c:257:16: error: 'sem_init' is deprecated [-Werror,-Wdeprecated-declarations] + "-Wno-error=deprecated-declarations" + # scsi-lowlevel.c:1244:11: error: cast from 'uint8_t *' (aka 'unsigned char *') to 'uint16_t *' (aka 'unsigned short *') increases required alignment from 1 to 2 [-Werror,-Wcast-align] + "-Wno-error=cast-align" + ]; }; meta = { From ccdcdd7cb6d70c998805f7bf0841328c6b346902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kier=C3=A1n=20Meinhardt?= Date: Tue, 24 Mar 2026 14:56:56 +0100 Subject: [PATCH 11/31] nixos/test-driver: provide machines, machines_qemu, machines_nspawn to testScript --- nixos/lib/test-driver/src/test_driver/driver.py | 1 + nixos/lib/test-script-prepend.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/nixos/lib/test-driver/src/test_driver/driver.py b/nixos/lib/test-driver/src/test_driver/driver.py index 8cbcb052be49..a457f7ed1be3 100644 --- a/nixos/lib/test-driver/src/test_driver/driver.py +++ b/nixos/lib/test-driver/src/test_driver/driver.py @@ -228,6 +228,7 @@ class Driver: general_symbols = dict( start_all=self.start_all, test_script=self.test_script, + machines=self.machines, machines_qemu=self.machines_qemu, machines_nspawn=self.machines_nspawn, vlans=self.vlans, diff --git a/nixos/lib/test-script-prepend.py b/nixos/lib/test-script-prepend.py index b9a12d5f2f54..d4b6d612e410 100644 --- a/nixos/lib/test-script-prepend.py +++ b/nixos/lib/test-script-prepend.py @@ -45,6 +45,8 @@ subtest: Callable[[str], ContextManager[None]] retry: RetryProtocol test_script: Callable[[], None] machines: List[BaseMachine] +machines_qemu: List[QemuMachine] +machines_nspawn: List[NspawnMachine] vlans: List[VLan] driver: Driver log: AbstractLogger From fd2eb432697f3cbb1aed941ca821753a1778584b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kier=C3=A1n=20Meinhardt?= Date: Tue, 24 Mar 2026 15:11:23 +0100 Subject: [PATCH 12/31] nixos/test-driver: provide QemuMachine, NspawnMachine types to testScript --- nixos/lib/test-driver/src/test_driver/driver.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/lib/test-driver/src/test_driver/driver.py b/nixos/lib/test-driver/src/test_driver/driver.py index a457f7ed1be3..67c0172c3f5d 100644 --- a/nixos/lib/test-driver/src/test_driver/driver.py +++ b/nixos/lib/test-driver/src/test_driver/driver.py @@ -244,6 +244,8 @@ class Driver: serial_stdout_on=self.serial_stdout_on, polling_condition=self.polling_condition, BaseMachine=BaseMachine, # for typing + QemuMachine=QemuMachine, # for typing + NspawnMachine=NspawnMachine, # for typing t=AssertionTester(), debug=self.debug, ) From 84fe17e86953dfb45b7b20b6eddc842fda562080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kier=C3=A1n=20Meinhardt?= Date: Tue, 24 Mar 2026 15:37:39 +0100 Subject: [PATCH 13/31] nixos/tests: fix machine type hints With the addition of test driver machines based on systemd-nspawn, the `Machine` class was superseded by an abstract base class `BaseMachine` and a subclass `QemuMachine` for conventional QEMU-based test nodes. This commit fixes the usage of the `Machine` class in type annotations across our tests. It also makes the tests use the more specific `machines_qemu` variable if QEMU-specific features are required from the machines. (`machines` provides only the methods shared by both `NspawnMachine` and `Qemumachine`.) --- nixos/tests/garage/common.nix | 12 ++++++------ nixos/tests/incus/incus_machine.py | 2 +- nixos/tests/networking-proxy.nix | 2 +- nixos/tests/pacemaker.nix | 6 +++--- .../tests/password-option-override-ordering.nix | 16 ++++++++-------- nixos/tests/rtkit.nix | 8 ++++---- nixos/tests/systemd-journal-upload.nix | 2 +- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/nixos/tests/garage/common.nix b/nixos/tests/garage/common.nix index c8392eee6dba..5928cc3c55a6 100644 --- a/nixos/tests/garage/common.nix +++ b/nixos/tests/garage/common.nix @@ -21,14 +21,14 @@ node_id: str host: str - def get_node_fqn(machine: Machine) -> GarageNode: + def get_node_fqn(machine: BaseMachine) -> GarageNode: node_id, host = machine.succeed("garage node id").split('@') return GarageNode(node_id=node_id, host=host) - def get_node_id(machine: Machine) -> str: + def get_node_id(machine: BaseMachine) -> str: return get_node_fqn(machine).node_id - def get_layout_version(machine: Machine) -> int: + def get_layout_version(machine: BaseMachine) -> int: version_data = machine.succeed("garage layout show") m = cur_version_regex.search(version_data) if m and m.group('ver') is not None: @@ -36,17 +36,17 @@ else: raise ValueError('Cannot find current layout version') - def apply_garage_layout(machine: Machine, layouts: List[str]): + def apply_garage_layout(machine: BaseMachine, layouts: List[str]): for layout in layouts: machine.succeed(f"garage layout assign {layout}") version = get_layout_version(machine) machine.succeed(f"garage layout apply --version {version}") - def create_api_key(machine: Machine, key_name: str) -> S3Key: + def create_api_key(machine: BaseMachine, key_name: str) -> S3Key: output = machine.succeed(f"garage key create {key_name}") return parse_api_key_data(output) - def get_api_key(machine: Machine, key_pattern: str) -> S3Key: + def get_api_key(machine: BaseMachine, key_pattern: str) -> S3Key: output = machine.succeed(f"garage key info {key_pattern}") return parse_api_key_data(output) diff --git a/nixos/tests/incus/incus_machine.py b/nixos/tests/incus/incus_machine.py index c25a7804b871..eeeb0bdecf4c 100644 --- a/nixos/tests/incus/incus_machine.py +++ b/nixos/tests/incus/incus_machine.py @@ -1,7 +1,7 @@ import json -class IncusHost(Machine): +class IncusHost(QemuMachine): def __init__(self, base): with subtest("Wait for startup"): base.wait_for_unit("incus.service") diff --git a/nixos/tests/networking-proxy.nix b/nixos/tests/networking-proxy.nix index 2f8a64b5affa..ec784f2bd657 100644 --- a/nixos/tests/networking-proxy.nix +++ b/nixos/tests/networking-proxy.nix @@ -72,7 +72,7 @@ in from typing import Dict, Optional - def get_machine_env(machine: Machine, user: Optional[str] = None) -> Dict[str, str]: + def get_machine_env(machine: BaseMachine, user: Optional[str] = None) -> Dict[str, str]: """ Gets the environment from a given machine, and returns it as a dictionary in the form: diff --git a/nixos/tests/pacemaker.nix b/nixos/tests/pacemaker.nix index 826c4be7df14..8023d6f14bc8 100644 --- a/nixos/tests/pacemaker.nix +++ b/nixos/tests/pacemaker.nix @@ -86,7 +86,7 @@ rec { output = node1.succeed("crm_resource -r cat --locate") match = re.search("is running on: (.+)", output) if match: - for machine in machines: + for machine in machines_qemu: if machine.name == match.group(1): current_node = machine break @@ -96,7 +96,7 @@ rec { current_node.crash() # pick another node that's still up - for machine in machines: + for machine in machines_qemu: if machine.booted: check_node = machine # find where the service has been started next @@ -105,7 +105,7 @@ rec { match = re.search("is running on: (.+)", output) # output will remain the old current_node until the crash is detected by pacemaker if match and match.group(1) != current_node.name: - for machine in machines: + for machine in machines_qemu: if machine.name == match.group(1): next_node = machine break diff --git a/nixos/tests/password-option-override-ordering.nix b/nixos/tests/password-option-override-ordering.nix index caa0246735ca..71c449064a0b 100644 --- a/nixos/tests/password-option-override-ordering.nix +++ b/nixos/tests/password-option-override-ordering.nix @@ -112,22 +112,22 @@ in assert stored_hash == pass_hash, f"{username} user password does not match" with subtest("alice user has correct password"): - for machine in machines: + for machine in machines_qemu: assert_password_sha512crypt_match(machine, "alice", "${password1}") assert "${hashed_sha512crypt}" not in machine.succeed("getent shadow alice"), f"{machine}: alice user password is not correct" with subtest("bob user has correct password"): - for machine in machines: + for machine in machines_qemu: print(machine.succeed("getent shadow bob")) assert "${hashed_bcrypt}" in machine.succeed("getent shadow bob"), f"{machine}: bob user password is not correct" with subtest("cat user has correct password"): - for machine in machines: + for machine in machines_qemu: print(machine.succeed("getent shadow cat")) assert "${hashed_bcrypt}" in machine.succeed("getent shadow cat"), f"{machine}: cat user password is not correct" with subtest("dan user has correct password"): - for machine in machines: + for machine in machines_qemu: print(machine.succeed("getent shadow dan")) assert "${hashed_bcrypt}" in machine.succeed("getent shadow dan"), f"{machine}: dan user password is not correct" @@ -138,11 +138,11 @@ in assert_password_sha512crypt_match(immutable, "greg", "${password1}") assert "${hashed_sha512crypt}" not in immutable.succeed("getent shadow greg"), "greg user password is not correct" - for machine in machines: + for machine in machines_qemu: machine.wait_for_unit("multi-user.target") machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'") - def check_login(machine: Machine, tty_number: str, username: str, password: str): + def check_login(machine: QemuMachine, tty_number: str, username: str, password: str): machine.send_key(f"alt-f{tty_number}") machine.wait_until_succeeds(f"[ $(fgconsole) = {tty_number} ]") machine.wait_for_unit(f"getty@tty{tty_number}.service") @@ -158,11 +158,11 @@ in assert username in machine.succeed(f"cat /tmp/{tty_number}"), f"{machine}: {username} password is not correct" with subtest("Test initialPassword override"): - for machine in machines: + for machine in machines_qemu: check_login(machine, "2", "egon", "${password1}") with subtest("Test initialHashedPassword override"): - for machine in machines: + for machine in machines_qemu: check_login(machine, "3", "fran", "meow") ''; } diff --git a/nixos/tests/rtkit.nix b/nixos/tests/rtkit.nix index d884a588a16e..74bf69e02a83 100644 --- a/nixos/tests/rtkit.nix +++ b/nixos/tests/rtkit.nix @@ -103,7 +103,7 @@ Result = namedtuple("Result", ["command", "machine", "status", "out", "value"]) Value = namedtuple("Value", ["type", "data"]) - def busctl(node: Machine, *args: Any, user: Optional[str] = None) -> Result: + def busctl(node: BaseMachine, *args: Any, user: Optional[str] = None) -> Result: command = f"busctl --json=short {shlex.join(map(str, args))}" if user is not None: command = f"su - {user} -c {shlex.quote(command)}" @@ -121,7 +121,7 @@ if result.status == 0: raise Exception(f"command `{result.command}` unexpectedly succeeded") - def rtkit_make_process_realtime(node: Machine, pid: int, priority: int, user: Optional[str] = None) -> Result: + def rtkit_make_process_realtime(node: BaseMachine, pid: int, priority: int, user: Optional[str] = None) -> Result: return busctl(node, "call", "org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1", "org.freedesktop.RealtimeKit1", "MakeThreadRealtimeWithPID", "ttu", pid, 0, priority, user=user) def get_max_realtime_priority() -> int: @@ -133,7 +133,7 @@ def parse_chrt(out: str, field: str) -> str: return next(map(lambda l: l.split(": ")[1], filter(lambda l: field in l, out.splitlines()))) - def get_pid(node: Machine, unit: str, user: Optional[str] = None) -> int: + def get_pid(node: BaseMachine, unit: str, user: Optional[str] = None) -> int: node.wait_for_unit(unit, user=user) (status, out) = node.systemctl(f"show -P MainPID {unit}", user=user) if status == 0: @@ -142,7 +142,7 @@ node.log(out) raise Exception(f"unable to determine MainPID of {unit} (systemctl exit code {status})") - def assert_sched(node: Machine, pid: int, policy: Optional[str] = None, priority: Optional[int] = None): + def assert_sched(node: BaseMachine, pid: int, policy: Optional[str] = None, priority: Optional[int] = None): out = node.succeed(f"chrt -p {pid}") node.log(out) if policy is not None: diff --git a/nixos/tests/systemd-journal-upload.nix b/nixos/tests/systemd-journal-upload.nix index 00dbba82b88b..af7817bd97d8 100644 --- a/nixos/tests/systemd-journal-upload.nix +++ b/nixos/tests/systemd-journal-upload.nix @@ -72,7 +72,7 @@ server.wait_for_unit("multi-user.target") client.wait_for_unit("multi-user.target") - def copy_pems(machine: Machine, domain: str): + def copy_pems(machine: BaseMachine, domain: str): machine.succeed("mkdir /run/secrets") machine.copy_from_host( source=f"{tmpdir}/{domain}/cert.pem", From ed182b540c16582c6d094f9a92fd8c09ac96d129 Mon Sep 17 00:00:00 2001 From: Adrian Pistol Date: Mon, 23 Mar 2026 14:15:26 +0100 Subject: [PATCH 14/31] wolfssl: 5.8.4 -> 5.9.0 --- pkgs/by-name/wo/wolfssl/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/wo/wolfssl/package.nix b/pkgs/by-name/wo/wolfssl/package.nix index e904c4dfefcc..52c0d752dc0a 100644 --- a/pkgs/by-name/wo/wolfssl/package.nix +++ b/pkgs/by-name/wo/wolfssl/package.nix @@ -17,13 +17,13 @@ }: stdenv.mkDerivation (finalAttrs: { pname = "wolfssl-${variant}"; - version = "5.8.4"; + version = "5.9.0"; src = fetchFromGitHub { owner = "wolfSSL"; repo = "wolfssl"; tag = "v${finalAttrs.version}-stable"; - hash = "sha256-vfJKmDdM0r591t5GnuSS7NyiUYXCQOTKbWLVydB3N9s="; + hash = "sha256-Ov59Zt0UskADQThdzr9wni2junSpy3jiABWpiGr8xtg="; }; postPatch = '' From b4bb9c85250fe78dee0aadbc727a3e4dec803bd8 Mon Sep 17 00:00:00 2001 From: r-vdp Date: Thu, 26 Mar 2026 12:24:37 +0100 Subject: [PATCH 15/31] nixos-rebuild-ng: redirect switch-to-configuration stdout to stderr nixos-rebuild-ng intentionally prints only the resulting store path to stdout so callers can script against it (see the print_result comment in services.py). However, switch-to-configuration's stdout was inherited unredirected, so any output from it or its children leaked into ours. See #503032 which accidentaly broke this contract. --- .../ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py | 6 ++++++ .../by-name/ni/nixos-rebuild-ng/src/tests/test_main.py | 10 ++++++++++ pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py index 95ebb3c9c0c2..0e89656ec7aa 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/nixos_rebuild/nix.py @@ -717,6 +717,12 @@ def switch_to_configuration( }, remote=target_host, sudo=sudo, + # switch-to-configuration is not expected to produce meaningful + # stdout, but if it (or any of its children) does, it would leak + # into our stdout and break the "only the store path on stdout" + # contract documented in services.py (see print_result). Redirect + # its stdout to our stderr defensively. + stdout=sys.stderr, ) diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py index 456a41f46eac..3ebba4edc5b6 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_main.py @@ -249,6 +249,7 @@ def test_execute_nix_boot(mock_run: Mock, tmp_path: Path) -> None: "boot", ], check=True, + stdout=ANY, **( DEFAULT_RUN_KWARGS | { @@ -547,6 +548,7 @@ def test_execute_nix_switch_flake(mock_run: Mock, tmp_path: Path) -> None: "switch", ], check=True, + stdout=ANY, **DEFAULT_RUN_KWARGS, ), ] @@ -795,6 +797,7 @@ def test_execute_nix_switch_build_target_host( "switch", ], check=True, + stdout=ANY, **DEFAULT_RUN_KWARGS, ), ] @@ -920,6 +923,7 @@ def test_execute_nix_switch_flake_target_host( "switch", ], check=True, + stdout=ANY, **DEFAULT_RUN_KWARGS, ), ] @@ -1047,6 +1051,7 @@ def test_execute_nix_switch_flake_build_host( "switch", ], check=True, + stdout=ANY, **DEFAULT_RUN_KWARGS, ), ] @@ -1132,6 +1137,7 @@ def test_execute_switch_rollback(mock_run: Mock, tmp_path: Path) -> None: "switch", ], check=True, + stdout=ANY, **DEFAULT_RUN_KWARGS, ), ] @@ -1307,6 +1313,7 @@ def test_execute_test_flake(mock_run: Mock, tmp_path: Path) -> None: call( [config_path / "bin/switch-to-configuration", "test"], check=True, + stdout=ANY, **DEFAULT_RUN_KWARGS, ), ] @@ -1372,6 +1379,7 @@ def test_execute_test_rollback( "test", ], check=True, + stdout=ANY, **DEFAULT_RUN_KWARGS, ), ] @@ -1433,6 +1441,7 @@ def test_execute_switch_store_path(mock_run: Mock, tmp_path: Path) -> None: "switch", ], check=True, + stdout=ANY, **( DEFAULT_RUN_KWARGS | { @@ -1551,6 +1560,7 @@ def test_execute_switch_store_path_target_host( "switch", ], check=True, + stdout=ANY, **DEFAULT_RUN_KWARGS, ), ] diff --git a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py index 28948d1eaaff..f8c6d75c5290 100644 --- a/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py +++ b/pkgs/by-name/ni/nixos-rebuild-ng/src/tests/test_nix.py @@ -770,6 +770,7 @@ def test_switch_to_configuration_without_systemd_run( }, sudo=False, remote=None, + stdout=sys.stderr, ) with pytest.raises(m.NixOSRebuildError) as e: @@ -811,6 +812,7 @@ def test_switch_to_configuration_without_systemd_run( }, sudo=True, remote=target_host, + stdout=sys.stderr, ) @@ -846,6 +848,7 @@ def test_switch_to_configuration_with_systemd_run( }, sudo=False, remote=None, + stdout=sys.stderr, ) target_host = m.Remote("user@localhost", [], None, "ssh") @@ -875,6 +878,7 @@ def test_switch_to_configuration_with_systemd_run( }, sudo=True, remote=target_host, + stdout=sys.stderr, ) From 98b37f726dcab9347ba1203ea0597d44942b80d9 Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Fri, 27 Mar 2026 03:33:36 +0000 Subject: [PATCH 16/31] bind: 9.20.18 -> 9.20.21 --- pkgs/by-name/bi/bind/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/bi/bind/package.nix b/pkgs/by-name/bi/bind/package.nix index c49ae029d7fa..0c4a7dd86461 100644 --- a/pkgs/by-name/bi/bind/package.nix +++ b/pkgs/by-name/bi/bind/package.nix @@ -29,11 +29,11 @@ stdenv.mkDerivation (finalAttrs: { pname = "bind"; - version = "9.20.18"; + version = "9.20.21"; src = fetchurl { url = "https://downloads.isc.org/isc/bind9/${finalAttrs.version}/bind-${finalAttrs.version}.tar.xz"; - hash = "sha256-38VGyZCsRRVSnNRcTdmVhisYrootDLKSCOiJal0yUzE="; + hash = "sha256-FeG1oifSiQ98ToI6bqAY3nDuLzoOhZy/89gqrYWQ3gM="; }; outputs = [ From 5fd1a7194edef843c2df3b3451c41169326dcaf1 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Sun, 29 Mar 2026 09:33:54 +0200 Subject: [PATCH 17/31] nixos/test-driver: fix create_machine return type This always returns a QemuMachine, which may need to have QEMU-specific methods called on it. Fixes: 23f1e6370d1e ("nixos/test-driver: add support for nspawn containers") Fixes: 799cafcc2338 ("nixos/test-driver: refactor Machine to BaseMachine and QemuMachine") --- nixos/lib/test-driver/src/test_driver/driver.py | 2 +- nixos/lib/test-script-prepend.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/lib/test-driver/src/test_driver/driver.py b/nixos/lib/test-driver/src/test_driver/driver.py index 8cbcb052be49..dacdfd015e10 100644 --- a/nixos/lib/test-driver/src/test_driver/driver.py +++ b/nixos/lib/test-driver/src/test_driver/driver.py @@ -369,7 +369,7 @@ class Driver: *, name: str | None = None, keep_machine_state: bool = False, - ) -> BaseMachine: + ) -> QemuMachine: """ Create a `QemuMachine`. This currently only supports qemu "nodes", not containers. """ diff --git a/nixos/lib/test-script-prepend.py b/nixos/lib/test-script-prepend.py index b9a12d5f2f54..dd9861f04441 100644 --- a/nixos/lib/test-script-prepend.py +++ b/nixos/lib/test-script-prepend.py @@ -36,7 +36,7 @@ class CreateMachineProtocol(Protocol): name: Optional[str] = None, keep_machine_state: bool = False, **kwargs: Any, # to allow usage of deprecated keep_vm_state - ) -> BaseMachine: + ) -> QemuMachine: raise Exception("This is just type information for the Nix test driver") From 4c3f8b7bb9d8dba42efefa9854a744fe811e420d Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Sun, 29 Mar 2026 13:27:57 +0200 Subject: [PATCH 18/31] busybox: fix potential build-time buffer overflow d8bcd4850391 ("fortify-headers: 1.1 -> 3.0.1") (currently on staging) breaks the musl build by identifying this issue. --- .../build-system-buffer-overflow.patch | 27 +++++++++++++++++++ pkgs/os-specific/linux/busybox/default.nix | 2 ++ 2 files changed, 29 insertions(+) create mode 100644 pkgs/os-specific/linux/busybox/build-system-buffer-overflow.patch diff --git a/pkgs/os-specific/linux/busybox/build-system-buffer-overflow.patch b/pkgs/os-specific/linux/busybox/build-system-buffer-overflow.patch new file mode 100644 index 000000000000..7fdb83a1596d --- /dev/null +++ b/pkgs/os-specific/linux/busybox/build-system-buffer-overflow.patch @@ -0,0 +1,27 @@ +From 3cf1ca7491bd5b6680e80355d76442ae14db681e Mon Sep 17 00:00:00 2001 +From: Alyssa Ross +Date: Sun, 29 Mar 2026 13:18:09 +0200 +Subject: [PATCH] build system: fix potential buffer overflow + +This could potentially write one byte past the end of line. +Identified by fortify-headers. +--- + scripts/basic/split-include.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/scripts/basic/split-include.c b/scripts/basic/split-include.c +index 6ef29195e..93011d511 100644 +--- a/scripts/basic/split-include.c ++++ b/scripts/basic/split-include.c +@@ -195,7 +195,7 @@ int main(int argc, const char * argv []) + ERROR_EXIT( "find" ); + + line[0] = '\n'; +- while (fgets(line+1, buffer_size, fp_find)) ++ while (fgets(line+1, buffer_size-1, fp_find)) + { + if (strstr(list_target, line) == NULL) + { +-- +2.53.0 + diff --git a/pkgs/os-specific/linux/busybox/default.nix b/pkgs/os-specific/linux/busybox/default.nix index 8402337be16d..677b884d1c6d 100644 --- a/pkgs/os-specific/linux/busybox/default.nix +++ b/pkgs/os-specific/linux/busybox/default.nix @@ -90,6 +90,8 @@ stdenv.mkDerivation rec { excludes = [ "networking/httpd_ratelimit_cgi.c" ]; # New since release. hash = "sha256-Msm9sDZrVx7ofunnvnTS73SPKUUpR3Tv5xZ/wBd+rts="; }) + # https://lists.busybox.net/pipermail/busybox/2026-March/092010.html + ./build-system-buffer-overflow.patch ] ++ lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) ./clang-cross.patch; From 9449884f4b0a660ffa8bc0d157b68fd407f9c3d7 Mon Sep 17 00:00:00 2001 From: sternenseemann Date: Wed, 18 Mar 2026 23:43:59 +0100 Subject: [PATCH 19/31] lixPackageSets.git.lix: 2.95.0-pre-20260317 -> 2.96.0-pre-20260318 --- pkgs/tools/package-management/lix/default.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/tools/package-management/lix/default.nix b/pkgs/tools/package-management/lix/default.nix index 1d95ef9a66ee..ecc3dc6c342e 100644 --- a/pkgs/tools/package-management/lix/default.nix +++ b/pkgs/tools/package-management/lix/default.nix @@ -283,14 +283,14 @@ lib.makeExtensible ( attrName = "git"; lix-args = rec { - version = "2.96.0-pre-20260317_${builtins.substring 0 12 src.rev}"; + version = "2.96.0-pre-20260318_${builtins.substring 0 12 src.rev}"; src = fetchFromGitea { domain = "git.lix.systems"; owner = "lix-project"; repo = "lix"; - rev = "96db7c79cf2a9a06725360b0d12e5de583bef07d"; - hash = "sha256-Ixwk38HArs7MZXxdWRkSZFzUhUdlCro+8+M/sO+fE/Y="; + rev = "8294cd534b2f01ee967b28aa73fcab1535d62b3d"; + hash = "sha256-BFijbNDCrfzpDdW+gNauP25QsTvEZ39dygWEI/RYeyY="; }; cargoDeps = rustPlatform.fetchCargoVendor { From b428932fc61d6d4b932fedf749bfcc0c5455f606 Mon Sep 17 00:00:00 2001 From: sternenseemann Date: Wed, 18 Mar 2026 23:33:55 +0100 Subject: [PATCH 20/31] nixVersions.*: patch for compatibility with lowdown >= 3.0 --- pkgs/tools/package-management/nix/default.nix | 47 ++++++++++++++----- .../lowdown-3.0-compat-2.28-2.30.patch | 27 +++++++++++ 2 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 pkgs/tools/package-management/nix/patches/lowdown-3.0-compat-2.28-2.30.patch diff --git a/pkgs/tools/package-management/nix/default.nix b/pkgs/tools/package-management/nix/default.nix index 8772d9df0b1e..1eb7c5699b06 100644 --- a/pkgs/tools/package-management/nix/default.nix +++ b/pkgs/tools/package-management/nix/default.nix @@ -134,6 +134,21 @@ let patches_common = lib.optional ( stdenv.system == "aarch64-darwin" ) ./patches/skip-flaky-darwin-tests.patch; + + # Lowdown 3.0 compatibility patch for nix 2.31–2.33; fetched from the + # upstream backport (same diff on every maintenance branch after + # fetchpatch strips metadata). Nix 2.34.4+ and the git snapshot + # already include the fix in their tagged source. + lowdown30Patch = pkgs.fetchpatch { + name = "nix-lowdown-3.0-support.patch"; + url = "https://github.com/NixOS/nix/commit/472c35c561bd9e8db1465e0677f1efe2cb88c568.patch"; + hash = "sha256-ZCQgI/euBN8t9rgdCsGRgrcEWG3T5MUc+bQc4tIcHuI="; + }; + + # Lowdown 3.0 compatibility patch for nix 2.28 and 2.30, which have a + # different markdown.cc layout (no LOWDOWN_TERM_NORELLINK branch) and + # never received an upstream backport. + lowdown30PatchOld = ./patches/lowdown-3.0-compat-2.28-2.30.patch; in lib.makeExtensible ( self: @@ -149,6 +164,7 @@ lib.makeExtensible ( url = "https://github.com/NixOS/nix/commit/5a64138e862fe364e751c5c286e8db8c466aaee7.patch?full_index=1"; hash = "sha256-vFv/D08x9urtoIE9wiC7Lln4Eq3sgNBwU7TBE1iyrfI="; }) + lowdown30PatchOld ]; }; @@ -172,22 +188,27 @@ lib.makeExtensible ( url = "https://github.com/NixOS/nix/commit/5cbd7856de0a9c13351f98e32a1e26d0854d87fd.patch?full_index=1"; hash = "sha256-r2ZF1zBZDKMvyX6X4VsaTMrg0zdjn59Jf6Hqg56r29E="; }) + lowdown30PatchOld ] ); nix_2_30 = addTests "nix_2_30" self.nixComponents_2_30.nix-everything; - nixComponents_2_31 = nixDependencies.callPackage ./modular/packages.nix rec { - version = "2.31.3"; - inherit (self.nix_2_30.meta) teams; - otherSplices = generateSplicesForNixComponents "nixComponents_2_31"; - src = fetchFromGitHub { - owner = "NixOS"; - repo = "nix"; - tag = version; - hash = "sha256-oe0YWe8f+pwQH4aYD2XXLW5iEHyXNUddurqJ5CUVCIk="; - }; - }; + nixComponents_2_31 = + (nixDependencies.callPackage ./modular/packages.nix rec { + version = "2.31.3"; + inherit (self.nix_2_30.meta) teams; + otherSplices = generateSplicesForNixComponents "nixComponents_2_31"; + src = fetchFromGitHub { + owner = "NixOS"; + repo = "nix"; + tag = version; + hash = "sha256-oe0YWe8f+pwQH4aYD2XXLW5iEHyXNUddurqJ5CUVCIk="; + }; + }).appendPatches + [ + lowdown30Patch + ]; nix_2_31 = addTests "nix_2_31" self.nixComponents_2_31.nix-everything; @@ -203,7 +224,7 @@ lib.makeExtensible ( hash = "sha256-5aH3xppfBs8j6P7A2wq8WQ05yJvlL7x0gQbWk4RN5eY="; }; }).appendPatches - patches_common; + (patches_common ++ [ lowdown30Patch ]); nix_2_32 = addTests "nix_2_32" self.nixComponents_2_32.nix-everything; @@ -219,7 +240,7 @@ lib.makeExtensible ( hash = "sha256-2Mga4e9ZtOPLwYqF4+hcjdsTImcA7TKUvDDfaF7jqEo="; }; }).appendPatches - patches_common; + (patches_common ++ [ lowdown30Patch ]); nix_2_33 = addTests "nix_2_33" self.nixComponents_2_33.nix-everything; diff --git a/pkgs/tools/package-management/nix/patches/lowdown-3.0-compat-2.28-2.30.patch b/pkgs/tools/package-management/nix/patches/lowdown-3.0-compat-2.28-2.30.patch new file mode 100644 index 000000000000..dd527f7f1cd6 --- /dev/null +++ b/pkgs/tools/package-management/nix/patches/lowdown-3.0-compat-2.28-2.30.patch @@ -0,0 +1,27 @@ +--- a/src/libcmd/markdown.cc ++++ b/src/libcmd/markdown.cc +@@ -37,7 +37,13 @@ + .vmargin = 0, + # endif + .feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES, +- .oflags = LOWDOWN_TERM_NOLINK, ++ .oflags = ++# if HAVE_LOWDOWN_3 ++ LOWDOWN_NOLINK ++# else ++ LOWDOWN_TERM_NOLINK ++# endif ++ , + }; + + auto doc = lowdown_doc_new(&opts); +--- a/src/libcmd/meson.build ++++ b/src/libcmd/meson.build +@@ -36,6 +36,7 @@ + configdata.set('HAVE_LOWDOWN', lowdown.found().to_int()) + # The API changed slightly around terminal initialization. + configdata.set('HAVE_LOWDOWN_1_4', lowdown.version().version_compare('>= 1.4.0').to_int()) ++configdata.set('HAVE_LOWDOWN_3', lowdown.version().version_compare('>= 3.0.0').to_int()) + + readline_flavor = get_option('readline-flavor') + if readline_flavor == 'editline' From bd8d2815089a0a6d4efdce39463b64ff95a56c6f Mon Sep 17 00:00:00 2001 From: sternenseemann Date: Wed, 18 Mar 2026 23:34:41 +0100 Subject: [PATCH 21/31] lixPackageSets.lix_2_93.lix: patch for compat with lowdown 3 --- pkgs/tools/package-management/lix/default.nix | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pkgs/tools/package-management/lix/default.nix b/pkgs/tools/package-management/lix/default.nix index ecc3dc6c342e..8ebcd187f4a3 100644 --- a/pkgs/tools/package-management/lix/default.nix +++ b/pkgs/tools/package-management/lix/default.nix @@ -41,6 +41,13 @@ let hash = "sha256-uu/SIG8fgVVWhsGxmszTPHwe4SQtLgbxdShOMKbeg2w="; }; + lixLowdown30Patch = fetchpatch { + name = "lix-lowdown-3.0-support.patch"; + url = "https://git.lix.systems/lix-project/lix/commit/af0390c27bdc401ece8f8192cb3024f0ff08e977.patch"; + excludes = [ "flake.nix" ]; + hash = "sha256-ZBkbgeZ/D7H2teX8bPy5NEG1aXbQVksTDV3aVBZdRPM="; + }; + makeLixScope = { attrName, @@ -229,6 +236,8 @@ lib.makeExtensible ( }) lixMdbookPatch + + lixLowdown30Patch ]; }; }; @@ -253,7 +262,10 @@ lib.makeExtensible ( hash = "sha256-APm8m6SVEAO17BBCka13u85/87Bj+LePP7Y3zHA3Mpg="; }; - patches = [ lixMdbookPatch ]; + patches = [ + lixMdbookPatch + lixLowdown30Patch + ]; }; }; From bfe44e60b5737b2dd63bfa873d93eb3fe96403c9 Mon Sep 17 00:00:00 2001 From: sternenseemann Date: Sat, 14 Mar 2026 11:31:51 +0100 Subject: [PATCH 22/31] lowdown: 2.0.4 -> 3.0.0 https://github.com/kristapsdz/lowdown/releases/tag/VERSION_3_0_0 --- pkgs/by-name/lo/lowdown/package.nix | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pkgs/by-name/lo/lowdown/package.nix b/pkgs/by-name/lo/lowdown/package.nix index d74416a0cba6..e754e0e09fdb 100644 --- a/pkgs/by-name/lo/lowdown/package.nix +++ b/pkgs/by-name/lo/lowdown/package.nix @@ -18,7 +18,7 @@ stdenv.mkDerivation rec { pname = "lowdown${ lib.optionalString (stdenv.hostPlatform.isDarwin && !enableDarwinSandbox) "-unsandboxed" }"; - version = "2.0.4"; + version = "3.0.0"; outputs = [ "out" @@ -29,7 +29,7 @@ stdenv.mkDerivation rec { src = fetchurl { url = "https://kristaps.bsd.lv/lowdown/snapshots/lowdown-${version}.tar.gz"; - sha512 = "649a508b7727df6e7e1203abb3853e05f167b64832fd5e1271f142ccf782e600b1de73c72dc02673d7b175effdc54f2c0f60318208a968af9f9763d09cf4f9ef"; + sha512 = "94e97234d598382c3c3dc27f9bfdb3a3a2fcf7dbb6a8df3c85ee09f27f792449034a41d49d9cfd3d8450d2de01b8562c20c3d120e65c81af4d7d6c9454119e93"; }; # https://github.com/kristapsdz/lowdown/pull/171 @@ -42,12 +42,6 @@ stdenv.mkDerivation rec { ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ fixDarwinDylibNames ]; - postPatch = '' - # fails test, some column width mismatch - rm regress/table-footnotes.md - rm regress/table-styles.md - ''; - # The Darwin sandbox calls fail inside Nix builds, presumably due to # being nested inside another sandbox. preConfigure = lib.optionalString (stdenv.hostPlatform.isDarwin && !enableDarwinSandbox) '' From f7803b4ebfb6addf6307250aa26af05e751880ed Mon Sep 17 00:00:00 2001 From: sternenseemann Date: Sat, 14 Mar 2026 11:32:22 +0100 Subject: [PATCH 23/31] lowdown: also test lix --- pkgs/by-name/lo/lowdown/package.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/by-name/lo/lowdown/package.nix b/pkgs/by-name/lo/lowdown/package.nix index e754e0e09fdb..6a4cadc42939 100644 --- a/pkgs/by-name/lo/lowdown/package.nix +++ b/pkgs/by-name/lo/lowdown/package.nix @@ -11,6 +11,7 @@ enableDarwinSandbox ? true, # for passthru.tests nix, + lix, lowdown-unsandboxed, }: @@ -107,7 +108,7 @@ stdenv.mkDerivation rec { passthru.tests = { # most important consumers in nixpkgs - inherit nix lowdown-unsandboxed; + inherit nix lix lowdown-unsandboxed; }; meta = { From 995f419d84f8693960dec300899b95e67010fcc2 Mon Sep 17 00:00:00 2001 From: sternenseemann Date: Sat, 14 Mar 2026 11:32:34 +0100 Subject: [PATCH 24/31] lowdown: sanity check output in installCheckPhase --- pkgs/by-name/lo/lowdown/package.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/by-name/lo/lowdown/package.nix b/pkgs/by-name/lo/lowdown/package.nix index 6a4cadc42939..ba2e5c7c505a 100644 --- a/pkgs/by-name/lo/lowdown/package.nix +++ b/pkgs/by-name/lo/lowdown/package.nix @@ -98,7 +98,7 @@ stdenv.mkDerivation rec { runHook preInstallCheck echo '# TEST' > test.md - $out/bin/lowdown test.md + $out/bin/lowdown test.md | grep '[hH]1' runHook postInstallCheck ''; From 58ed53d264d4756c31d0f9559552d8be216645ab Mon Sep 17 00:00:00 2001 From: sternenseemann Date: Wed, 18 Mar 2026 23:15:36 +0100 Subject: [PATCH 25/31] lowdown: 3.0.0 -> 3.0.1 https://github.com/kristapsdz/lowdown/releases/tag/VERSION_3_0_1 --- pkgs/by-name/lo/lowdown/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/lo/lowdown/package.nix b/pkgs/by-name/lo/lowdown/package.nix index ba2e5c7c505a..ad56e0e6991d 100644 --- a/pkgs/by-name/lo/lowdown/package.nix +++ b/pkgs/by-name/lo/lowdown/package.nix @@ -19,7 +19,7 @@ stdenv.mkDerivation rec { pname = "lowdown${ lib.optionalString (stdenv.hostPlatform.isDarwin && !enableDarwinSandbox) "-unsandboxed" }"; - version = "3.0.0"; + version = "3.0.1"; outputs = [ "out" @@ -30,7 +30,7 @@ stdenv.mkDerivation rec { src = fetchurl { url = "https://kristaps.bsd.lv/lowdown/snapshots/lowdown-${version}.tar.gz"; - sha512 = "94e97234d598382c3c3dc27f9bfdb3a3a2fcf7dbb6a8df3c85ee09f27f792449034a41d49d9cfd3d8450d2de01b8562c20c3d120e65c81af4d7d6c9454119e93"; + sha512 = "fe68e1b7ff23f3992398356d7aa9a330dfd7b72e22bea9a91eeef74182b209ecea0c9f3e2b2216e1a07b2358da2b746238ec9cbbdeebdd3551cef14dd2d79f46"; }; # https://github.com/kristapsdz/lowdown/pull/171 From 94f41998acf1c602223fb480c69841beb60e0046 Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Mon, 30 Mar 2026 22:19:47 +0100 Subject: [PATCH 26/31] ruff: 0.15.7 -> 0.15.8 Changes: https://github.com/astral-sh/ruff/releases/tag/0.15.8 --- pkgs/by-name/ru/ruff/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ru/ruff/package.nix b/pkgs/by-name/ru/ruff/package.nix index 4eb1a03897a4..fbb2d76bb659 100644 --- a/pkgs/by-name/ru/ruff/package.nix +++ b/pkgs/by-name/ru/ruff/package.nix @@ -16,18 +16,18 @@ rustPlatform.buildRustPackage (finalAttrs: { pname = "ruff"; - version = "0.15.7"; + version = "0.15.8"; src = fetchFromGitHub { owner = "astral-sh"; repo = "ruff"; tag = finalAttrs.version; - hash = "sha256-aDRFNJKvxuHPYaZtoM+93DxJGsTPMLKGBH5QhIiTh0Y="; + hash = "sha256-hjllQ7lw2hecrwhPQlcq97LLxFYR28vCY14Ty6eBox4="; }; cargoBuildFlags = [ "--package=ruff" ]; - cargoHash = "sha256-m3VkHXhjemXVOFFVSUOVz0xD2Rc2pMsP+dnMYQD29uI="; + cargoHash = "sha256-iQKX+TqfP9r5P+UJKLqNysHYJm8N6qCCDfs8KYSb1vY="; nativeBuildInputs = [ installShellFiles ]; From cb7060e5a72071bee5aace4de99764bca1c10b05 Mon Sep 17 00:00:00 2001 From: zowoq <59103226+zowoq@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:51:52 +0000 Subject: [PATCH 27/31] linux_testing: 7.0-rc5 -> 7.0-rc6 --- pkgs/os-specific/linux/kernel/kernels-org.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/os-specific/linux/kernel/kernels-org.json b/pkgs/os-specific/linux/kernel/kernels-org.json index 8eaa7261baa0..74f25ba7841d 100644 --- a/pkgs/os-specific/linux/kernel/kernels-org.json +++ b/pkgs/os-specific/linux/kernel/kernels-org.json @@ -1,7 +1,7 @@ { "testing": { - "version": "7.0-rc5", - "hash": "sha256:00q4scz3kyrbd8v23pjdzgmaz9scmxg10cqlfwrrd7xj0hxp3pah", + "version": "7.0-rc6", + "hash": "sha256:085bc06mbav64rmsm6a34j2y75nrqpqp4qd8ld8mb9acf1i4iw45", "lts": false }, "6.1": { From 8795f02446b0ab489fb1dbe2596f762917d078b9 Mon Sep 17 00:00:00 2001 From: zowoq <59103226+zowoq@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:51:56 +0000 Subject: [PATCH 28/31] linux_6_19: 6.19.10 -> 6.19.11 --- pkgs/os-specific/linux/kernel/kernels-org.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/os-specific/linux/kernel/kernels-org.json b/pkgs/os-specific/linux/kernel/kernels-org.json index 74f25ba7841d..354cb61beac5 100644 --- a/pkgs/os-specific/linux/kernel/kernels-org.json +++ b/pkgs/os-specific/linux/kernel/kernels-org.json @@ -35,8 +35,8 @@ "lts": true }, "6.19": { - "version": "6.19.10", - "hash": "sha256:072s76238rnf87yhdy15nbxfyq7x3ch7p2v14dq4pq551qd48va6", + "version": "6.19.11", + "hash": "sha256:16ymkc5r3hw05z7l7ih3qw406qlszz1l7b4g5yz0hv15ddxrs0r0", "lts": false } } From 8aef3ab2a02393e6b2b1543c04c14cb4602835e0 Mon Sep 17 00:00:00 2001 From: zowoq <59103226+zowoq@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:51:58 +0000 Subject: [PATCH 29/31] linux_6_18: 6.18.20 -> 6.18.21 --- pkgs/os-specific/linux/kernel/kernels-org.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/os-specific/linux/kernel/kernels-org.json b/pkgs/os-specific/linux/kernel/kernels-org.json index 354cb61beac5..bbb78a26f30d 100644 --- a/pkgs/os-specific/linux/kernel/kernels-org.json +++ b/pkgs/os-specific/linux/kernel/kernels-org.json @@ -30,8 +30,8 @@ "lts": true }, "6.18": { - "version": "6.18.20", - "hash": "sha256:0lrm76rdlr92kjq3g410qdff9v49mpdf400lmsh7hq74k2ymlyl3", + "version": "6.18.21", + "hash": "sha256:0ks735y6jq4yy3jaicjsj4dn4n3kk2skf9dqh9dyifipn57j2f0w", "lts": true }, "6.19": { From f3191fe7cbbd9fa7a14d2f3abf05e36f87030d3e Mon Sep 17 00:00:00 2001 From: zowoq <59103226+zowoq@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:52:01 +0000 Subject: [PATCH 30/31] linux_6_12: 6.12.78 -> 6.12.80 --- pkgs/os-specific/linux/kernel/kernels-org.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/os-specific/linux/kernel/kernels-org.json b/pkgs/os-specific/linux/kernel/kernels-org.json index bbb78a26f30d..431e2d87dcdc 100644 --- a/pkgs/os-specific/linux/kernel/kernels-org.json +++ b/pkgs/os-specific/linux/kernel/kernels-org.json @@ -25,8 +25,8 @@ "lts": true }, "6.12": { - "version": "6.12.78", - "hash": "sha256:0gdgykr4nqk1dzb5ms2m07saxx58zvacpfv8ynhfrv7snjs835vi", + "version": "6.12.80", + "hash": "sha256:0lrylj87bb8ky29pbplpncrfhmgqqbq3d49iqgdwv7p7jvc929f9", "lts": true }, "6.18": { From 838104fb57ae49cdb78d8bc542822a6222f4352e Mon Sep 17 00:00:00 2001 From: zowoq <59103226+zowoq@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:52:03 +0000 Subject: [PATCH 31/31] linux_6_6: 6.6.130 -> 6.6.132 --- pkgs/os-specific/linux/kernel/kernels-org.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/os-specific/linux/kernel/kernels-org.json b/pkgs/os-specific/linux/kernel/kernels-org.json index 431e2d87dcdc..adf23aea9dbc 100644 --- a/pkgs/os-specific/linux/kernel/kernels-org.json +++ b/pkgs/os-specific/linux/kernel/kernels-org.json @@ -20,8 +20,8 @@ "lts": true }, "6.6": { - "version": "6.6.130", - "hash": "sha256:139480lyi3if8pd2j3yld5a01lk7113kbcn2kxpzyk29p5kslq14", + "version": "6.6.132", + "hash": "sha256:1d1fdd5wpphlm68yb16csaaijv0lf38ynl3gpvvdspsg22xjpdn5", "lts": true }, "6.12": {