Skip to content
16 changes: 16 additions & 0 deletions nixos/doc/manual/release-notes/rl-1809.xml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,22 @@ $ nix-instantiate -E '(import <nixpkgsunstable> {}).gitFull'
from your config without any issues.
</para>
</listitem>
<listitem>
<para>
The following changes apply if <literal>system.stateVersion</literal> is
changed to 18.09 or higher. For <literal>system.stateVersion = "18.03"</literal> or lower
the old behavior is preserved.
</para>
<itemizedlist>
<listitem>
<para>
If predictable interface names are enabled (<literal>networking.usePredictableInterfaceNames = true </literal>),
interfaces are already renamed in Stage 1 (initrd). <emphasis role="strong">This may break existing configurations
that require networking in Stage 1 (initrd) to boot. In the worst case, your system may become unbootable.</emphasis>
</para>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</section>

Expand Down
3 changes: 3 additions & 0 deletions nixos/modules/services/hardware/udev.nix
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ in
change randomly across reboots; for instance, you may find
<literal>eth0</literal> and <literal>eth1</literal> flipping
unpredictably.
For configurations with <literal>system.stateVersion >= 18.09</literal>,
interfaces are renamed to predictable names in Stage 1 (initrd).
Changing this option's value may require a reboot to take effect.
'';
};

Expand Down
3 changes: 3 additions & 0 deletions nixos/modules/system/boot/networkd.nix
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,10 @@ in
systemd.services.systemd-networkd = {
wantedBy = [ "multi-user.target" ];
restartTriggers = map (f: f.source) (unitFiles);
} // optionalAttrs (versionOlder config.system.stateVersion "18.09") {
# prevent race condition with interface renaming (#39069)
# This is not necessary for newer configs (stateVersion>=18.09)
# since they do interface renaming in stage 1
requires = [ "systemd-udev-settle.service" ];
after = [ "systemd-udev-settle.service" ];
};
Expand Down
10 changes: 9 additions & 1 deletion nixos/modules/system/boot/stage-1.nix
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,11 @@ let
fi
''; # */

# use same rules as stage 2 for renaming network interfaces to predictable names
netSetupLinkRules = ../../services/hardware/80-net-setup-link.rules;

udevRules = pkgs.runCommand "udev-rules"
{ allowedReferences = [ extraUtils ]; }
{ allowedReferences = [ netSetupLinkRules extraUtils ]; }
''
mkdir -p $out

Expand All @@ -206,6 +208,12 @@ let
cp -v ${udev}/lib/udev/rules.d/60-cdrom_id.rules $out/
cp -v ${udev}/lib/udev/rules.d/60-persistent-storage.rules $out/
cp -v ${udev}/lib/udev/rules.d/80-drivers.rules $out/
# rename interfaces to predictable names in stage 1 for newer configs
${optionalString (config.networking.usePredictableInterfaceNames &&
lib.versionAtLeast config.system.stateVersion "18.09") ''
cp -v ${udev}/lib/udev/rules.d/75-net-description.rules $out/
cp -v ${netSetupLinkRules} $out/80-net-setup-link.rules
''}
cp -v ${pkgs.lvm2}/lib/udev/rules.d/*.rules $out/
${config.boot.initrd.extraUdevRulesCommands}

Expand Down
2 changes: 1 addition & 1 deletion nixos/release.nix
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ in rec {
tests.hydra = callTest tests/hydra {};
tests.i3wm = callTest tests/i3wm.nix {};
tests.iftop = callTest tests/iftop.nix {};
tests.initrd-network-ssh = callTest tests/initrd-network-ssh {};
tests.initrd-network-ssh = callSubTests tests/initrd-network-ssh {};
tests.installer = callSubTests tests/installer.nix {};
tests.influxdb = callTest tests/influxdb.nix {};
tests.ipv6 = callTest tests/ipv6.nix {};
Expand Down
123 changes: 70 additions & 53 deletions nixos/tests/initrd-network-ssh/default.nix
Original file line number Diff line number Diff line change
@@ -1,59 +1,76 @@
import ../make-test.nix ({ lib, ... }:
{ system ? builtins.currentSystem }:

{
name = "initrd-network-ssh";
meta = with lib.maintainers; {
maintainers = [ willibutz ];
};
let
inherit (import ../../lib/testing.nix { inherit system; }) makeTest pkgs;
in
with pkgs; lib.listToAttrs ( map (predictable: {

nodes = with lib; rec {
server =
{ config, ... }:
{
boot.kernelParams = [
"ip=${config.networking.primaryIPAddress}:::255.255.255.0::eth1:none"
];
boot.initrd.network = {
enable = true;
ssh = {
enable = true;
authorizedKeys = [ "${readFile ./openssh.pub}" ];
port = 22;
hostRSAKey = ./dropbear.priv;
};
};
boot.initrd.preLVMCommands = ''
while true; do
if [ -f fnord ]; then
poweroff
fi
sleep 1
done
'';
name = if predictable then "predictable" else "unpredictable";

value = makeTest {
name = "initrd-network-ssh"+ lib.optionalString predictable "predictable";
meta = with lib.maintainers; {
maintainers = [ willibutz xeji ];
};

client =
{ config, ... }:
{
environment.etc.knownHosts = {
text = concatStrings [
"server,"
"${toString (head (splitString " " (
toString (elemAt (splitString "\n" config.networking.extraHosts) 2)
)))} "
"${readFile ./dropbear.pub}"
];
};
nodes = with lib;
let interface2 = if predictable then "ens4" else "eth1";
in rec {
server =
{ config, ... }:
{
# for stateVersion < 18.09 this doesn't make a difference in initrd
system.stateVersion = "18.09";
networking.usePredictableInterfaceNames = mkForce predictable;
boot.kernelParams = [
"ip=${config.networking.primaryIPAddress}:::255.255.255.0::${interface2}:none"
];
boot.initrd.network = {
enable = true;
# for initrd networking with predictable interface names,
# udhcpc must be given the name as an argument
udhcpc.extraArgs = [ (lib.optionalString predictable "-i ${interface2}")];
ssh = {
enable = true;
authorizedKeys = [ "${readFile ./openssh.pub}" ];
port = 22;
hostRSAKey = ./dropbear.priv;
};
};
boot.initrd.preLVMCommands = ''
while true; do
if [ -f fnord ]; then
poweroff
fi
sleep 1
done
'';
};

client =
{ config, ... }:
{
environment.etc.knownHosts = {
text = concatStrings [
"server,"
"${toString (head (splitString " " (
toString (elemAt (splitString "\n" config.networking.extraHosts) 2)
)))} "
"${readFile ./dropbear.pub}"
];
};
};
};
};

testScript = ''
startAll;
$client->waitForUnit("network.target");
$client->copyFileFromHost("${./openssh.priv}","/etc/sshKey");
$client->succeed("chmod 0600 /etc/sshKey");
$client->waitUntilSucceeds("ping -c 1 server");
$client->succeed("ssh -i /etc/sshKey -o UserKnownHostsFile=/etc/knownHosts server 'touch /fnord'");
$client->shutdown;
'';
})
testScript = ''
$client->waitForUnit("network.target");
$client->copyFileFromHost("${./openssh.priv}","/etc/sshKey");
$client->succeed("chmod 0600 /etc/sshKey");
$server->start;
$client->waitUntilSucceeds("ping -c 1 server");
$client->succeed("ssh -i /etc/sshKey -o UserKnownHostsFile=/etc/knownHosts server 'touch /fnord'");
$client->shutdown;
'';
};
}) [false true]
)
13 changes: 9 additions & 4 deletions nixos/tests/predictable-interface-names.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

let
inherit (import ../lib/testing.nix { inherit system; }) makeTest pkgs;
in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: {
in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: stateV: {
name = pkgs.lib.optionalString (!predictable) "un" + "predictable"
+ pkgs.lib.optionalString withNetworkd "Networkd";
+ pkgs.lib.optionalString withNetworkd "Networkd"
+ builtins.replaceStrings ["."] [""] stateV;
value = makeTest {
name = "${if predictable then "" else "un"}predictableInterfaceNames${if withNetworkd then "-with-networkd" else ""}";
name = "${if predictable then "" else "un"}predictableInterfaceNames${if withNetworkd then "-with-networkd" else ""}-${stateV}";
meta = {};

machine = { lib, ... }: {
networking.usePredictableInterfaceNames = lib.mkForce predictable;
networking.useNetworkd = withNetworkd;
networking.dhcpcd.enable = !withNetworkd;
# So we can see the initrd device names in the log
# although we don't add an automated test here
boot.initrd.network.enable = (stateV == "18.09");
system.stateVersion = stateV;
};

testScript = ''
Expand All @@ -21,4 +26,4 @@ in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: {
$machine->fail("ip link show ${if predictable then "eth0" else "ens3"}");
'';
};
}) [[true false] [true false]])
}) [[true false] [true false] ["18.03" "18.09"]])