From 71983a6eb5cdf0755d80d8197f52f52b0a5d3f9f Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Sun, 17 Apr 2022 18:05:25 -0400 Subject: systemd-initrd: Don't use SYSTEMD_SULOGIN_FORCE --- nixos/modules/system/boot/systemd/initrd.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'nixos') diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index ffe96f3ad9c30..a566fbeae441c 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -378,7 +378,7 @@ in { "/etc/systemd/system.conf".text = '' [Manager] - DefaultEnvironment=PATH=/bin:/sbin ${optionalString (isBool cfg.emergencyAccess && cfg.emergencyAccess) "SYSTEMD_SULOGIN_FORCE=1"} + DefaultEnvironment=PATH=/bin:/sbin ${cfg.extraConfig} ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)} ''; @@ -389,7 +389,10 @@ in { "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules; "/etc/passwd".source = "${pkgs.fakeNss}/etc/passwd"; - "/etc/shadow".text = "root:${if isBool cfg.emergencyAccess then "!" else cfg.emergencyAccess}:::::::"; + # We can use either ! or * to lock the root account in the + # console, but some software like OpenSSH won't even allow you + # to log in with an SSH key if you use ! so we use * instead + "/etc/shadow".text = "root:${if isBool cfg.emergencyAccess then optionalString (!cfg.emergencyAccess) "*" else cfg.emergencyAccess}:::::::"; "/bin".source = "${initrdBinEnv}/bin"; "/sbin".source = "${initrdBinEnv}/sbin"; -- cgit 1.4.1 From fef26d88e20cc4045768071421bf495871506b7a Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Mon, 8 Aug 2022 19:09:37 -0400 Subject: systemd-initrd: Support secrets when boot loader doesn't initrd-secrets: Fix service config with systemd-stage-1 --- nixos/modules/system/boot/systemd/initrd-secrets.nix | 4 ++-- nixos/modules/virtualisation/qemu-vm.nix | 2 ++ nixos/tests/initrd-network-openvpn/default.nix | 2 +- nixos/tests/initrd-network-ssh/default.nix | 4 ---- 4 files changed, 5 insertions(+), 7 deletions(-) (limited to 'nixos') diff --git a/nixos/modules/system/boot/systemd/initrd-secrets.nix b/nixos/modules/system/boot/systemd/initrd-secrets.nix index bc65880719d7a..7b59c0cbe7b84 100644 --- a/nixos/modules/system/boot/systemd/initrd-secrets.nix +++ b/nixos/modules/system/boot/systemd/initrd-secrets.nix @@ -19,13 +19,13 @@ # drop this service, we'd mount the /run tmpfs over the secret, making it # invisible in stage 2. script = '' - for secret in $(cd /.initrd-secrets; find . -type f); do + for secret in $(cd /.initrd-secrets; find . -type f -o -type l); do mkdir -p "$(dirname "/$secret")" cp "/.initrd-secrets/$secret" "/$secret" done ''; - unitConfig = { + serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index a55a21a46a538..727dca36fe019 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -880,6 +880,8 @@ in boot.initrd.kernelModules = optionals (cfg.useNixStoreImage && !cfg.writableStore) [ "erofs" ]; + boot.loader.supportsInitrdSecrets = mkIf (!cfg.useBootLoader) (mkVMOverride false); + boot.initrd.extraUtilsCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable) '' # We need mke2fs in the initrd. diff --git a/nixos/tests/initrd-network-openvpn/default.nix b/nixos/tests/initrd-network-openvpn/default.nix index dbb34c28eea74..1150e2dc5b0d2 100644 --- a/nixos/tests/initrd-network-openvpn/default.nix +++ b/nixos/tests/initrd-network-openvpn/default.nix @@ -26,7 +26,7 @@ import ../make-test-python.nix ({ lib, ...}: enable = true; openvpn = { enable = true; - configuration = "/dev/null"; + configuration = builtins.toFile "initrd.ovpn" ""; }; }; }; diff --git a/nixos/tests/initrd-network-ssh/default.nix b/nixos/tests/initrd-network-ssh/default.nix index 0ad0563b0ce15..017de6882081d 100644 --- a/nixos/tests/initrd-network-ssh/default.nix +++ b/nixos/tests/initrd-network-ssh/default.nix @@ -22,10 +22,6 @@ import ../make-test-python.nix ({ lib, ... }: hostKeys = [ ./ssh_host_ed25519_key ]; }; }; - boot.initrd.extraUtilsCommands = '' - mkdir -p $out/secrets/etc/ssh - cat "${./ssh_host_ed25519_key}" > $out/secrets/etc/ssh/sh_host_ed25519_key - ''; boot.initrd.preLVMCommands = '' while true; do if [ -f fnord ]; then -- cgit 1.4.1 From 762b69f2ff4a7e843a7e9690a0bed741f229229d Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Fri, 22 Jul 2022 01:26:24 -0400 Subject: systemd-initrd: Fix up root directory mode --- nixos/modules/system/boot/stage-1.nix | 3 ++- pkgs/build-support/kernel/make-initrd-ng.nix | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'nixos') diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index d26ea7597c450..1229f6357523f 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -445,7 +445,8 @@ let ) config.boot.initrd.secrets) } - (cd "$tmp" && find . -print0 | sort -z | bsdtar --uid 0 --gid 0 -cnf - -T - | bsdtar --null -cf - --format=newc @-) | \ + # mindepth 1 so that we don't change the mode of / + (cd "$tmp" && find . -mindepth 1 -print0 | sort -z | bsdtar --uid 0 --gid 0 -cnf - -T - | bsdtar --null -cf - --format=newc @-) | \ ${compressorExe} ${lib.escapeShellArgs initialRamdisk.compressorArgs} >> "$1" ''; diff --git a/pkgs/build-support/kernel/make-initrd-ng.nix b/pkgs/build-support/kernel/make-initrd-ng.nix index dc0e9b87db2ab..f3cf3d59f92b0 100644 --- a/pkgs/build-support/kernel/make-initrd-ng.nix +++ b/pkgs/build-support/kernel/make-initrd-ng.nix @@ -78,14 +78,14 @@ in STRIP = if strip then "${pkgsBuildHost.binutils.targetPrefix}strip" else null; }) '' - mkdir ./root + mkdir -p ./root/var/empty make-initrd-ng "$contentsPath" ./root mkdir "$out" (cd root && find * .[^.*] -exec touch -h -d '@1' '{}' +) for PREP in $prepend; do cat $PREP >> $out/initrd done - (cd root && find * .[^.*] -print0 | sort -z | cpio -o -H newc -R +0:+0 --reproducible --null | eval -- $compress >> "$out/initrd") + (cd root && find . -print0 | sort -z | cpio -o -H newc -R +0:+0 --reproducible --null | eval -- $compress >> "$out/initrd") if [ -n "$makeUInitrd" ]; then mkimage -A "$uInitrdArch" -O linux -T ramdisk -C "$uInitrdCompression" -d "$out/initrd" $out/initrd.img -- cgit 1.4.1 From e70b42bf612e65693c95fab37ff0de725858ed8e Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Fri, 22 Jul 2022 01:32:22 -0400 Subject: systemd-initrd: Add users and groups with static IDs. --- nixos/modules/config/users-groups.nix | 92 ++++++++++++++++++++++++++++ nixos/modules/system/boot/systemd/initrd.nix | 1 - nixos/tests/systemd-initrd-simple.nix | 2 + 3 files changed, 94 insertions(+), 1 deletion(-) (limited to 'nixos') diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix index e44cce11f3a8b..d1e9c8072eac4 100644 --- a/nixos/modules/config/users-groups.nix +++ b/nixos/modules/config/users-groups.nix @@ -428,6 +428,8 @@ let uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.users) "uid"; gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.groups) "gid"; + sdInitrdUidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) config.boot.initrd.systemd.users) "uid"; + sdInitrdGidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) config.boot.initrd.systemd.groups) "gid"; spec = pkgs.writeText "users-groups.json" (builtins.toJSON { inherit (cfg) mutableUsers; @@ -534,6 +536,54 @@ in { WARNING: enabling this can lock you out of your system. Enable this only if you know what are you doing. ''; }; + + # systemd initrd + boot.initrd.systemd.users = mkOption { + visible = false; + description = '' + Users to include in initrd. + ''; + default = {}; + type = types.attrsOf (types.submodule ({ name, ... }: { + options.uid = mkOption { + visible = false; + type = types.int; + description = '' + ID of the user in initrd. + ''; + defaultText = literalExpression "config.users.users.\${name}.uid"; + default = cfg.users.${name}.uid; + }; + options.group = mkOption { + visible = false; + type = types.singleLineStr; + description = '' + Group the user belongs to in initrd. + ''; + defaultText = literalExpression "config.users.users.\${name}.group"; + default = cfg.users.${name}.group; + }; + })); + }; + + boot.initrd.systemd.groups = mkOption { + visible = false; + description = '' + Groups to include in initrd. + ''; + default = {}; + type = types.attrsOf (types.submodule ({ name, ... }: { + options.gid = mkOption { + visible = false; + type = types.int; + description = '' + ID of the group in initrd. + ''; + defaultText = literalExpression "config.users.groups.\${name}.gid"; + default = cfg.groups.${name}.gid; + }; + })); + }; }; @@ -639,10 +689,52 @@ in { "/etc/profiles/per-user/$USER" ]; + # systemd initrd + boot.initrd.systemd = lib.mkIf config.boot.initrd.systemd.enable { + contents = { + "/etc/passwd".text = '' + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: { uid, group }: let + g = config.boot.initrd.systemd.groups.${group}; + in "${n}:x:${toString uid}:${toString g.gid}::/var/empty:") config.boot.initrd.systemd.users)} + ''; + "/etc/group".text = '' + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: { gid }: "${n}:x:${toString gid}:") config.boot.initrd.systemd.groups)} + ''; + }; + + users = { + root = {}; + nobody = {}; + }; + + groups = { + root = {}; + nogroup = {}; + systemd-journal = {}; + tty = {}; + dialout = {}; + kmem = {}; + input = {}; + video = {}; + render = {}; + sgx = {}; + audio = {}; + video = {}; + lp = {}; + disk = {}; + cdrom = {}; + tape = {}; + kvm = {}; + }; + }; + assertions = [ { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique); message = "UIDs and GIDs must be unique!"; } + { assertion = !cfg.enforceIdUniqueness || (sdInitrdUidsAreUnique && sdInitrdGidsAreUnique); + message = "systemd initrd UIDs and GIDs must be unique!"; + } { # If mutableUsers is false, to prevent users creating a # configuration that locks them out of the system, ensure that # there is at least one "privileged" account that has a diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index a566fbeae441c..e11ab5c824d2a 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -388,7 +388,6 @@ in { "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules; - "/etc/passwd".source = "${pkgs.fakeNss}/etc/passwd"; # We can use either ! or * to lock the root account in the # console, but some software like OpenSSH won't even allow you # to log in with an SSH key if you use ! so we use * instead diff --git a/nixos/tests/systemd-initrd-simple.nix b/nixos/tests/systemd-initrd-simple.nix index f7f4863d17e35..a6a22e9d48e06 100644 --- a/nixos/tests/systemd-initrd-simple.nix +++ b/nixos/tests/systemd-initrd-simple.nix @@ -27,6 +27,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { machine.succeed("[ -e /dev/pts/ptmx ]") # /dev/pts machine.succeed("[ -e /run/keys ]") # /run/keys + with subtest("groups work"): + machine.fail("journalctl -b 0 | grep 'systemd-udevd.*Unknown group.*ignoring'") with subtest("growfs works"): oldAvail = machine.succeed("df --output=avail / | sed 1d") -- cgit 1.4.1 From dd392d7c7694c762812f84b4d0a3ba8157ac8a73 Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Wed, 29 Jun 2022 01:01:59 -0400 Subject: systemd-initrd: networkd --- nixos/modules/services/hardware/udev.nix | 11 --- nixos/modules/system/boot/networkd.nix | 132 ++++++++++++++++++++++----- nixos/modules/system/boot/systemd/initrd.nix | 9 -- nixos/tests/all-tests.nix | 1 + nixos/tests/predictable-interface-names.nix | 39 ++++++-- nixos/tests/systemd-initrd-networkd.nix | 45 +++++++++ 6 files changed, 184 insertions(+), 53 deletions(-) create mode 100644 nixos/tests/systemd-initrd-networkd.nix (limited to 'nixos') diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix index d95261332419d..95c2a4fc5c3e1 100644 --- a/nixos/modules/services/hardware/udev.nix +++ b/nixos/modules/services/hardware/udev.nix @@ -16,16 +16,6 @@ let ''; - # networkd link files are used early by udev to set up interfaces early. - # This must be done in stage 1 to avoid race conditions between udev and - # network daemons. - # TODO move this into the initrd-network module when it exists - initrdLinkUnits = pkgs.runCommand "initrd-link-units" {} '' - mkdir -p $out - ln -s ${udev}/lib/systemd/network/*.link $out/ - ${lib.concatMapStringsSep "\n" (file: "ln -s ${file} $out/") (lib.mapAttrsToList (n: v: "${v.unit}/${n}") (lib.filterAttrs (n: _: hasSuffix ".link" n) config.systemd.network.units))} - ''; - extraUdevRules = pkgs.writeTextFile { name = "extra-udev-rules"; text = cfg.extraRules; @@ -398,7 +388,6 @@ in systemd = config.boot.initrd.systemd.package; binPackages = config.boot.initrd.services.udev.binPackages ++ [ config.boot.initrd.systemd.contents."/bin".source ]; }; - "/etc/systemd/network".source = initrdLinkUnits; }; # Insert initrd rules boot.initrd.services.udev.packages = [ diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 05a667a09efc1..e325a33a9e377 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -6,8 +6,6 @@ with lib; let - cfg = config.systemd.network; - check = { global = { @@ -2941,14 +2939,12 @@ let + def.extraConfig; }; - unitFiles = listToAttrs (map (name: { - name = "systemd/network/${name}"; + mkUnitFiles = prefix: cfg: listToAttrs (map (name: { + name = "${prefix}systemd/network/${name}"; value.source = "${cfg.units.${name}.unit}/${name}"; }) (attrNames cfg.units)); -in -{ - options = { + commonOptions = { systemd.network.enable = mkOption { default = false; @@ -3051,12 +3047,11 @@ in }; - config = mkMerge [ + commonConfig = config: let cfg = config.systemd.network; in mkMerge [ # .link units are honored by udev, no matter if systemd-networkd is enabled or not. { systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links; - environment.etc = unitFiles; systemd.network.wait-online.extraArgs = [ "--timeout=${toString cfg.wait-online.timeout}" ] @@ -3066,14 +3061,6 @@ in (mkIf config.systemd.network.enable { - users.users.systemd-network.group = "systemd-network"; - - systemd.additionalUpstreamSystemUnits = [ - "systemd-networkd-wait-online.service" - "systemd-networkd.service" - "systemd-networkd.socket" - ]; - systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks; @@ -3082,14 +3069,6 @@ in # networkd. systemd.sockets.systemd-networkd.wantedBy = [ "sockets.target" ]; - systemd.services.systemd-networkd = { - wantedBy = [ "multi-user.target" ]; - aliases = [ "dbus-org.freedesktop.network1.service" ]; - restartTriggers = map (x: x.source) (attrValues unitFiles) ++ [ - config.environment.etc."systemd/networkd.conf".source - ]; - }; - systemd.services.systemd-networkd-wait-online = { inherit (cfg.wait-online) enable; wantedBy = [ "network-online.target" ]; @@ -3111,8 +3090,37 @@ in }; }; + }) + ]; + + stage2Config = let + cfg = config.systemd.network; + unitFiles = mkUnitFiles "" cfg; + in mkMerge [ + (commonConfig config) + + { environment.etc = unitFiles; } + + (mkIf config.systemd.network.enable { + + users.users.systemd-network.group = "systemd-network"; + + systemd.additionalUpstreamSystemUnits = [ + "systemd-networkd-wait-online.service" + "systemd-networkd.service" + "systemd-networkd.socket" + ]; + environment.etc."systemd/networkd.conf" = renderConfig cfg.config; + systemd.services.systemd-networkd = { + wantedBy = [ "multi-user.target" ]; + restartTriggers = map (x: x.source) (attrValues unitFiles) ++ [ + config.environment.etc."systemd/networkd.conf".source + ]; + aliases = [ "dbus-org.freedesktop.network1.service" ]; + }; + networking.iproute2 = mkIf (cfg.config.addRouteTablesToIPRoute2 && cfg.config.routeTables != { }) { enable = mkDefault true; rttablesExtraConfig = '' @@ -3123,6 +3131,80 @@ in }; services.resolved.enable = mkDefault true; + + }) + ]; + + stage1Config = let + cfg = config.boot.initrd.systemd.network; + in mkMerge [ + (commonConfig config.boot.initrd) + + { + systemd.network.enable = mkDefault config.boot.initrd.network.enable; + systemd.contents = mkUnitFiles "/etc/" cfg; + + # Networkd link files are used early by udev to set up interfaces early. + # This must be done in stage 1 to avoid race conditions between udev and + # network daemons. + systemd.network.units = lib.filterAttrs (n: _: hasSuffix ".link" n) config.systemd.network.units; + systemd.storePaths = ["${config.boot.initrd.systemd.package}/lib/systemd/network/99-default.link"]; + } + + (mkIf cfg.enable { + + systemd.package = pkgs.systemdStage1Network; + + systemd.additionalUpstreamUnits = [ + "systemd-networkd-wait-online.service" + "systemd-networkd.service" + "systemd-networkd.socket" + "systemd-network-generator.service" + "network-online.target" + "network-pre.target" + "network.target" + "nss-lookup.target" + "nss-user-lookup.target" + "remote-fs-pre.target" + "remote-fs.target" + ]; + systemd.users.systemd-network = {}; + systemd.groups.systemd-network = {}; + + systemd.contents."/etc/systemd/networkd.conf" = renderConfig cfg.config; + + systemd.services.systemd-networkd.wantedBy = [ "initrd.target" ]; + systemd.services.systemd-network-generator.wantedBy = [ "sysinit.target" ]; + + systemd.storePaths = [ + "${config.boot.initrd.systemd.package}/lib/systemd/systemd-networkd" + "${config.boot.initrd.systemd.package}/lib/systemd/systemd-networkd-wait-online" + "${config.boot.initrd.systemd.package}/lib/systemd/systemd-network-generator" + ]; + kernelModules = [ "af_packet" ]; + + }) + ]; + +in + +{ + options = commonOptions // { + boot.initrd = commonOptions; + }; + + config = mkMerge [ + stage2Config + (mkIf config.boot.initrd.systemd.enable { + assertions = [{ + assertion = config.boot.initrd.network.udhcpc.extraArgs == []; + message = '' + boot.initrd.network.udhcpc.extraArgs is not supported when + boot.initrd.systemd.enable is enabled + ''; + }]; + + boot.initrd = stage1Config; }) ]; } diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index e11ab5c824d2a..d623eddf699f9 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -72,15 +72,6 @@ let "systemd-tmpfiles-setup.service" "timers.target" "umount.target" - - # TODO: Networking - # "network-online.target" - # "network-pre.target" - # "network.target" - # "nss-lookup.target" - # "nss-user-lookup.target" - # "remote-fs-pre.target" - # "remote-fs.target" ] ++ cfg.additionalUpstreamUnits; upstreamWants = [ diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 0783f3bf68e25..00a637b460f54 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -677,6 +677,7 @@ in { systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {}; systemd-initrd-swraid = handleTest ./systemd-initrd-swraid.nix {}; systemd-initrd-vconsole = handleTest ./systemd-initrd-vconsole.nix {}; + systemd-initrd-networkd = handleTest ./systemd-initrd-networkd.nix {}; systemd-journal = handleTest ./systemd-journal.nix {}; systemd-machinectl = handleTest ./systemd-machinectl.nix {}; systemd-networkd = handleTest ./systemd-networkd.nix {}; diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix index 684df9c39246c..42183625c7c93 100644 --- a/nixos/tests/predictable-interface-names.nix +++ b/nixos/tests/predictable-interface-names.nix @@ -8,25 +8,48 @@ let testCombinations = pkgs.lib.cartesianProductOfSets { predictable = [true false]; withNetworkd = [true false]; + systemdStage1 = [true false]; }; -in pkgs.lib.listToAttrs (builtins.map ({ predictable, withNetworkd }: { +in pkgs.lib.listToAttrs (builtins.map ({ predictable, withNetworkd, systemdStage1 }: { name = pkgs.lib.optionalString (!predictable) "un" + "predictable" - + pkgs.lib.optionalString withNetworkd "Networkd"; + + pkgs.lib.optionalString withNetworkd "Networkd" + + pkgs.lib.optionalString systemdStage1 "SystemdStage1"; value = makeTest { - name = "${pkgs.lib.optionalString (!predictable) "un"}predictableInterfaceNames${pkgs.lib.optionalString withNetworkd "-with-networkd"}"; + name = pkgs.lib.optionalString (!predictable) "un" + "predictableInterfaceNames" + + pkgs.lib.optionalString withNetworkd "-with-networkd" + + pkgs.lib.optionalString systemdStage1 "-systemd-stage-1"; meta = {}; - nodes.machine = { lib, ... }: { + nodes.machine = { lib, ... }: let + script = '' + ip link + if ${lib.optionalString predictable "!"} ip link show eth0; then + echo Success + else + exit 1 + fi + ''; + in { networking.usePredictableInterfaceNames = lib.mkForce predictable; networking.useNetworkd = withNetworkd; networking.dhcpcd.enable = !withNetworkd; networking.useDHCP = !withNetworkd; # Check if predictable interface names are working in stage-1 - boot.initrd.postDeviceCommands = '' - ip link - ip link show eth0 ${if predictable then "&&" else "||"} exit 1 - ''; + boot.initrd.postDeviceCommands = script; + + boot.initrd.systemd = lib.mkIf systemdStage1 { + enable = true; + initrdBin = [ pkgs.iproute2 ]; + services.systemd-udev-settle.wantedBy = ["initrd.target"]; + services.check-interfaces = { + requiredBy = ["initrd.target"]; + after = ["systemd-udev-settle.service"]; + serviceConfig.Type = "oneshot"; + path = [ pkgs.iproute2 ]; + inherit script; + }; + }; }; testScript = '' diff --git a/nixos/tests/systemd-initrd-networkd.nix b/nixos/tests/systemd-initrd-networkd.nix new file mode 100644 index 0000000000000..872a8cd64b25d --- /dev/null +++ b/nixos/tests/systemd-initrd-networkd.nix @@ -0,0 +1,45 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "systemd-initrd-network"; + meta.maintainers = [ lib.maintainers.elvishjerricco ]; + + nodes = { + basic = { ... }: { + boot.initrd.network.enable = true; + + boot.initrd.systemd = { + enable = true; + network.networks."99-eth0" = { + matchConfig.Name = "eth0"; + DHCP = "yes"; + }; + network.wait-online.timeout = 10; + # Drop the boot into emergency mode if we timeout + targets.network-online.requiredBy = [ "initrd.target" ]; + services.systemd-networkd-wait-online.requiredBy = + [ "network-online.target" ]; + + initrdBin = [ pkgs.iproute2 pkgs.iputils pkgs.gnugrep ]; + services.check = { + requiredBy = [ "initrd.target" ]; + before = [ "initrd.target" ]; + after = [ "network-online.target" ]; + serviceConfig.Type = "oneshot"; + path = [ pkgs.iproute2 pkgs.iputils pkgs.gnugrep ]; + script = '' + ip addr | grep 10.0.2.15 || exit 1 + ping -c1 10.0.2.2 || exit 1 + ''; + }; + }; + }; + }; + + testScript = '' + start_all() + basic.wait_for_unit("multi-user.target") + # Make sure the systemd-network user was set correctly in initrd + basic.succeed("[ $(stat -c '%U,%G' /run/systemd/netif/links) = systemd-network,systemd-network ]") + basic.succeed("ip addr show >&2") + basic.succeed("ip route show >&2") + ''; +}) -- cgit 1.4.1 From 748f1329fcee7db22be526677089719a5276ba30 Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Sun, 16 Oct 2022 19:59:41 -0400 Subject: systemd-initrd: Automatically configure networking.interfaces --- nixos/modules/tasks/network-interfaces-systemd.nix | 302 +++++++++++---------- nixos/tests/systemd-initrd-networkd.nix | 7 +- 2 files changed, 161 insertions(+), 148 deletions(-) (limited to 'nixos') diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix index b24b29c32d4ac..0fcd3c10219c1 100644 --- a/nixos/modules/tasks/network-interfaces-systemd.nix +++ b/nixos/modules/tasks/network-interfaces-systemd.nix @@ -28,11 +28,164 @@ let # TODO: warn the user that any address configured on those interfaces will be useless ++ concatMap (i: attrNames (filterAttrs (_: config: config.type != "internal") i.interfaces)) (attrValues cfg.vswitches); + domains = cfg.search ++ (optional (cfg.domain != null) cfg.domain); + genericNetwork = override: + let gateway = optional (cfg.defaultGateway != null && (cfg.defaultGateway.address or "") != "") cfg.defaultGateway.address + ++ optional (cfg.defaultGateway6 != null && (cfg.defaultGateway6.address or "") != "") cfg.defaultGateway6.address; + makeGateway = gateway: { + routeConfig = { + Gateway = gateway; + GatewayOnLink = false; + }; + }; + in optionalAttrs (gateway != [ ]) { + routes = override (map makeGateway gateway); + } // optionalAttrs (domains != [ ]) { + domains = override domains; + }; + + genericDhcpNetworks = initrd: mkIf cfg.useDHCP { + networks."99-ethernet-default-dhcp" = { + # We want to match physical ethernet interfaces as commonly + # found on laptops, desktops and servers, to provide an + # "out-of-the-box" setup that works for common cases. This + # heuristic isn't perfect (it could match interfaces with + # custom names that _happen_ to start with en or eth), but + # should be good enough to make the common case easy and can + # be overridden on a case-by-case basis using + # higher-priority networks or by disabling useDHCP. + + # Type=ether matches veth interfaces as well, and this is + # more likely to result in interfaces being configured to + # use DHCP when they shouldn't. + + # When wait-online.anyInterface is enabled, RequiredForOnline really + # means "sufficient for online", so we can enable it. + # Otherwise, don't block the network coming online because of default networks. + matchConfig.Name = ["en*" "eth*"]; + DHCP = "yes"; + linkConfig.RequiredForOnline = + lib.mkDefault (if initrd + then config.boot.initrd.systemd.network.wait-online.anyInterface + else config.systemd.network.wait-online.anyInterface); + networkConfig.IPv6PrivacyExtensions = "kernel"; + }; + networks."99-wireless-client-dhcp" = { + # Like above, but this is much more likely to be correct. + matchConfig.WLANInterfaceType = "station"; + DHCP = "yes"; + linkConfig.RequiredForOnline = + lib.mkDefault config.systemd.network.wait-online.anyInterface; + networkConfig.IPv6PrivacyExtensions = "kernel"; + # We also set the route metric to one more than the default + # of 1024, so that Ethernet is preferred if both are + # available. + dhcpV4Config.RouteMetric = 1025; + ipv6AcceptRAConfig.RouteMetric = 1025; + }; + }; + + + interfaceNetworks = mkMerge (forEach interfaces (i: { + netdevs = mkIf i.virtual ({ + "40-${i.name}" = { + netdevConfig = { + Name = i.name; + Kind = i.virtualType; + }; + "${i.virtualType}Config" = optionalAttrs (i.virtualOwner != null) { + User = i.virtualOwner; + }; + }; + }); + networks."40-${i.name}" = mkMerge [ (genericNetwork id) { + name = mkDefault i.name; + DHCP = mkForce (dhcpStr + (if i.useDHCP != null then i.useDHCP else false)); + address = forEach (interfaceIps i) + (ip: "${ip.address}/${toString ip.prefixLength}"); + routes = forEach (interfaceRoutes i) + (route: { + # Most of these route options have not been tested. + # Please fix or report any mistakes you may find. + routeConfig = + optionalAttrs (route.address != null && route.prefixLength != null) { + Destination = "${route.address}/${toString route.prefixLength}"; + } // + optionalAttrs (route.options ? fastopen_no_cookie) { + FastOpenNoCookie = route.options.fastopen_no_cookie; + } // + optionalAttrs (route.via != null) { + Gateway = route.via; + } // + optionalAttrs (route.type != null) { + Type = route.type; + } // + optionalAttrs (route.options ? onlink) { + GatewayOnLink = true; + } // + optionalAttrs (route.options ? initrwnd) { + InitialAdvertisedReceiveWindow = route.options.initrwnd; + } // + optionalAttrs (route.options ? initcwnd) { + InitialCongestionWindow = route.options.initcwnd; + } // + optionalAttrs (route.options ? pref) { + IPv6Preference = route.options.pref; + } // + optionalAttrs (route.options ? mtu) { + MTUBytes = route.options.mtu; + } // + optionalAttrs (route.options ? metric) { + Metric = route.options.metric; + } // + optionalAttrs (route.options ? src) { + PreferredSource = route.options.src; + } // + optionalAttrs (route.options ? protocol) { + Protocol = route.options.protocol; + } // + optionalAttrs (route.options ? quickack) { + QuickAck = route.options.quickack; + } // + optionalAttrs (route.options ? scope) { + Scope = route.options.scope; + } // + optionalAttrs (route.options ? from) { + Source = route.options.from; + } // + optionalAttrs (route.options ? table) { + Table = route.options.table; + } // + optionalAttrs (route.options ? advmss) { + TCPAdvertisedMaximumSegmentSize = route.options.advmss; + } // + optionalAttrs (route.options ? ttl-propagate) { + TTLPropagate = route.options.ttl-propagate == "enabled"; + }; + }); + networkConfig.IPv6PrivacyExtensions = "kernel"; + linkConfig = optionalAttrs (i.macAddress != null) { + MACAddress = i.macAddress; + } // optionalAttrs (i.mtu != null) { + MTUBytes = toString i.mtu; + }; + }]; + })); + in { + config = mkMerge [ - config = mkIf cfg.useNetworkd { + (mkIf config.boot.initrd.network.enable { + # Note this is if initrd.network.enable, not if + # initrd.systemd.network.enable. By setting the latter and not the + # former, the user retains full control over the configuration. + boot.initrd.systemd.network = mkMerge [(genericDhcpNetworks true) interfaceNetworks]; + }) + + (mkIf cfg.useNetworkd { assertions = [ { assertion = cfg.defaultGatewayWindowSize == null; @@ -54,149 +207,11 @@ in networking.dhcpcd.enable = mkDefault false; systemd.network = - let - domains = cfg.search ++ (optional (cfg.domain != null) cfg.domain); - genericNetwork = override: - let gateway = optional (cfg.defaultGateway != null && (cfg.defaultGateway.address or "") != "") cfg.defaultGateway.address - ++ optional (cfg.defaultGateway6 != null && (cfg.defaultGateway6.address or "") != "") cfg.defaultGateway6.address; - makeGateway = gateway: { - routeConfig = { - Gateway = gateway; - GatewayOnLink = false; - }; - }; - in optionalAttrs (gateway != [ ]) { - routes = override (map makeGateway gateway); - } // optionalAttrs (domains != [ ]) { - domains = override domains; - }; - in mkMerge [ { + mkMerge [ { enable = true; } - (mkIf cfg.useDHCP { - networks."99-ethernet-default-dhcp" = lib.mkIf cfg.useDHCP { - # We want to match physical ethernet interfaces as commonly - # found on laptops, desktops and servers, to provide an - # "out-of-the-box" setup that works for common cases. This - # heuristic isn't perfect (it could match interfaces with - # custom names that _happen_ to start with en or eth), but - # should be good enough to make the common case easy and can - # be overridden on a case-by-case basis using - # higher-priority networks or by disabling useDHCP. - - # Type=ether matches veth interfaces as well, and this is - # more likely to result in interfaces being configured to - # use DHCP when they shouldn't. - - # When wait-online.anyInterface is enabled, RequiredForOnline really - # means "sufficient for online", so we can enable it. - # Otherwise, don't block the network coming online because of default networks. - matchConfig.Name = ["en*" "eth*"]; - DHCP = "yes"; - linkConfig.RequiredForOnline = - lib.mkDefault config.systemd.network.wait-online.anyInterface; - networkConfig.IPv6PrivacyExtensions = "kernel"; - }; - networks."99-wireless-client-dhcp" = lib.mkIf cfg.useDHCP { - # Like above, but this is much more likely to be correct. - matchConfig.WLANInterfaceType = "station"; - DHCP = "yes"; - linkConfig.RequiredForOnline = - lib.mkDefault config.systemd.network.wait-online.anyInterface; - networkConfig.IPv6PrivacyExtensions = "kernel"; - # We also set the route metric to one more than the default - # of 1024, so that Ethernet is preferred if both are - # available. - dhcpV4Config.RouteMetric = 1025; - ipv6AcceptRAConfig.RouteMetric = 1025; - }; - }) - (mkMerge (forEach interfaces (i: { - netdevs = mkIf i.virtual ({ - "40-${i.name}" = { - netdevConfig = { - Name = i.name; - Kind = i.virtualType; - }; - "${i.virtualType}Config" = optionalAttrs (i.virtualOwner != null) { - User = i.virtualOwner; - }; - }; - }); - networks."40-${i.name}" = mkMerge [ (genericNetwork id) { - name = mkDefault i.name; - DHCP = mkForce (dhcpStr - (if i.useDHCP != null then i.useDHCP else false)); - address = forEach (interfaceIps i) - (ip: "${ip.address}/${toString ip.prefixLength}"); - routes = forEach (interfaceRoutes i) - (route: { - # Most of these route options have not been tested. - # Please fix or report any mistakes you may find. - routeConfig = - optionalAttrs (route.address != null && route.prefixLength != null) { - Destination = "${route.address}/${toString route.prefixLength}"; - } // - optionalAttrs (route.options ? fastopen_no_cookie) { - FastOpenNoCookie = route.options.fastopen_no_cookie; - } // - optionalAttrs (route.via != null) { - Gateway = route.via; - } // - optionalAttrs (route.type != null) { - Type = route.type; - } // - optionalAttrs (route.options ? onlink) { - GatewayOnLink = true; - } // - optionalAttrs (route.options ? initrwnd) { - InitialAdvertisedReceiveWindow = route.options.initrwnd; - } // - optionalAttrs (route.options ? initcwnd) { - InitialCongestionWindow = route.options.initcwnd; - } // - optionalAttrs (route.options ? pref) { - IPv6Preference = route.options.pref; - } // - optionalAttrs (route.options ? mtu) { - MTUBytes = route.options.mtu; - } // - optionalAttrs (route.options ? metric) { - Metric = route.options.metric; - } // - optionalAttrs (route.options ? src) { - PreferredSource = route.options.src; - } // - optionalAttrs (route.options ? protocol) { - Protocol = route.options.protocol; - } // - optionalAttrs (route.options ? quickack) { - QuickAck = route.options.quickack; - } // - optionalAttrs (route.options ? scope) { - Scope = route.options.scope; - } // - optionalAttrs (route.options ? from) { - Source = route.options.from; - } // - optionalAttrs (route.options ? table) { - Table = route.options.table; - } // - optionalAttrs (route.options ? advmss) { - TCPAdvertisedMaximumSegmentSize = route.options.advmss; - } // - optionalAttrs (route.options ? ttl-propagate) { - TTLPropagate = route.options.ttl-propagate == "enabled"; - }; - }); - networkConfig.IPv6PrivacyExtensions = "kernel"; - linkConfig = optionalAttrs (i.macAddress != null) { - MACAddress = i.macAddress; - } // optionalAttrs (i.mtu != null) { - MTUBytes = toString i.mtu; - }; - }]; - }))) + (genericDhcpNetworks false) + interfaceNetworks (mkMerge (flip mapAttrsToList cfg.bridges (name: bridge: { netdevs."40-${name}" = { netdevConfig = { @@ -437,6 +452,7 @@ in bindsTo = [ "systemd-networkd.service" ]; }; }; - }; + }) + ]; } diff --git a/nixos/tests/systemd-initrd-networkd.nix b/nixos/tests/systemd-initrd-networkd.nix index 872a8cd64b25d..1656f5d5e1898 100644 --- a/nixos/tests/systemd-initrd-networkd.nix +++ b/nixos/tests/systemd-initrd-networkd.nix @@ -8,12 +8,9 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { boot.initrd.systemd = { enable = true; - network.networks."99-eth0" = { - matchConfig.Name = "eth0"; - DHCP = "yes"; - }; + # Enable network-online to fail the test in case of timeout network.wait-online.timeout = 10; - # Drop the boot into emergency mode if we timeout + network.wait-online.anyInterface = true; targets.network-online.requiredBy = [ "initrd.target" ]; services.systemd-networkd-wait-online.requiredBy = [ "network-online.target" ]; -- cgit 1.4.1 From 0698a1cf04392a24f740b4b5e16c5bd33642b6df Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Wed, 3 Aug 2022 06:36:11 -0400 Subject: systemd-initrd: sshd --- nixos/modules/system/boot/initrd-ssh.nix | 64 ++++++++++++++++++---- nixos/tests/all-tests.nix | 1 + nixos/tests/systemd-initrd-networkd-ssh.nix | 82 +++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 nixos/tests/systemd-initrd-networkd-ssh.nix (limited to 'nixos') diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix index 125f75d667069..60c5ff62ffff0 100644 --- a/nixos/modules/system/boot/initrd-ssh.nix +++ b/nixos/modules/system/boot/initrd-ssh.nix @@ -5,6 +5,10 @@ with lib; let cfg = config.boot.initrd.network.ssh; + shell = if cfg.shell == null then "/bin/ash" else cfg.shell; + inherit (config.programs.ssh) package; + + enabled = let initrd = config.boot.initrd; in (initrd.network.enable || initrd.systemd.network.enable) && cfg.enable; in @@ -33,8 +37,9 @@ in }; shell = mkOption { - type = types.str; - default = "/bin/ash"; + type = types.nullOr types.str; + default = null; + defaultText = ''"/bin/ash"''; description = lib.mdDoc '' Login shell of the remote user. Can be used to limit actions user can do. ''; @@ -119,9 +124,11 @@ in sshdCfg = config.services.openssh; sshdConfig = '' + UsePAM no Port ${toString cfg.port} PasswordAuthentication no + AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys2 /etc/ssh/authorized_keys.d/%u ChallengeResponseAuthentication no ${flip concatMapStrings cfg.hostKeys (path: '' @@ -142,7 +149,7 @@ in ${cfg.extraConfig} ''; - in mkIf (config.boot.initrd.network.enable && cfg.enable) { + in mkIf enabled { assertions = [ { assertion = cfg.authorizedKeys != []; @@ -157,14 +164,19 @@ in for instructions. ''; } + + { + assertion = config.boot.initrd.systemd.enable -> cfg.shell == null; + message = "systemd stage 1 does not support boot.initrd.network.ssh.shell"; + } ]; - boot.initrd.extraUtilsCommands = '' - copy_bin_and_libs ${pkgs.openssh}/bin/sshd + boot.initrd.extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable) '' + copy_bin_and_libs ${package}/bin/sshd cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib ''; - boot.initrd.extraUtilsCommandsTest = '' + boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) '' # sshd requires a host key to check config, so we pass in the test's tmpkey="$(mktemp initrd-ssh-testkey.XXXXXXXXXX)" cp "${../../../tests/initrd-network-ssh/ssh_host_ed25519_key}" "$tmpkey" @@ -176,9 +188,9 @@ in rm "$tmpkey" ''; - boot.initrd.network.postCommands = '' - echo '${cfg.shell}' > /etc/shells - echo 'root:x:0:0:root:/root:${cfg.shell}' > /etc/passwd + boot.initrd.network.postCommands = mkIf (!config.boot.initrd.systemd.enable) '' + echo '${shell}' > /etc/shells + echo 'root:x:0:0:root:/root:${shell}' > /etc/passwd echo 'sshd:x:1:1:sshd:/var/empty:/bin/nologin' >> /etc/passwd echo 'passwd: files' > /etc/nsswitch.conf @@ -204,7 +216,7 @@ in /bin/sshd -e ''; - boot.initrd.postMountCommands = '' + boot.initrd.postMountCommands = mkIf (!config.boot.initrd.systemd.enable) '' # Stop sshd cleanly before stage 2. # # If you want to keep it around to debug post-mount SSH issues, @@ -217,6 +229,38 @@ in boot.initrd.secrets = listToAttrs (map (path: nameValuePair (initrdKeyPath path) path) cfg.hostKeys); + + # Systemd initrd stuff + boot.initrd.systemd = mkIf config.boot.initrd.systemd.enable { + users.sshd = { uid = 1; group = "sshd"; }; + groups.sshd = { gid = 1; }; + + contents."/etc/ssh/authorized_keys.d/root".text = + concatStringsSep "\n" config.boot.initrd.network.ssh.authorizedKeys; + contents."/etc/ssh/sshd_config".text = sshdConfig; + storePaths = ["${package}/bin/sshd"]; + + services.sshd = { + description = "SSH Daemon"; + wantedBy = ["initrd.target"]; + after = ["network.target" "initrd-nixos-copy-secrets.service"]; + + # Keys from Nix store are world-readable, which sshd doesn't + # like. If this were a real nix store and not the initrd, we + # neither would nor could do this + preStart = flip concatMapStrings cfg.hostKeys (path: '' + /bin/chmod 0600 "${initrdKeyPath path}" + ''); + unitConfig.DefaultDependencies = false; + serviceConfig = { + ExecStart = "${package}/bin/sshd -D -f /etc/ssh/sshd_config"; + Type = "simple"; + KillMode = "process"; + Restart = "on-failure"; + }; + }; + }; + }; } diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 00a637b460f54..5771d1c3bc96d 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -678,6 +678,7 @@ in { systemd-initrd-swraid = handleTest ./systemd-initrd-swraid.nix {}; systemd-initrd-vconsole = handleTest ./systemd-initrd-vconsole.nix {}; systemd-initrd-networkd = handleTest ./systemd-initrd-networkd.nix {}; + systemd-initrd-networkd-ssh = handleTest ./systemd-initrd-networkd-ssh.nix {}; systemd-journal = handleTest ./systemd-journal.nix {}; systemd-machinectl = handleTest ./systemd-machinectl.nix {}; systemd-networkd = handleTest ./systemd-networkd.nix {}; diff --git a/nixos/tests/systemd-initrd-networkd-ssh.nix b/nixos/tests/systemd-initrd-networkd-ssh.nix new file mode 100644 index 0000000000000..943552613be99 --- /dev/null +++ b/nixos/tests/systemd-initrd-networkd-ssh.nix @@ -0,0 +1,82 @@ +import ./make-test-python.nix ({ lib, ... }: { + name = "systemd-initrd-network-ssh"; + meta.maintainers = [ lib.maintainers.elvishjerricco ]; + + nodes = with lib; { + server = { config, pkgs, ... }: { + environment.systemPackages = [pkgs.cryptsetup]; + boot.loader.systemd-boot.enable = true; + boot.loader.timeout = 0; + virtualisation = { + emptyDiskImages = [ 4096 ]; + useBootLoader = true; + useEFIBoot = true; + }; + + specialisation.encrypted-root.configuration = { + virtualisation.bootDevice = "/dev/mapper/root"; + boot.initrd.luks.devices = lib.mkVMOverride { + root.device = "/dev/vdc"; + }; + boot.initrd.systemd.enable = true; + boot.initrd.network = { + enable = true; + ssh = { + enable = true; + authorizedKeys = [ (readFile ./initrd-network-ssh/id_ed25519.pub) ]; + port = 22; + # Terrible hack so it works with useBootLoader + hostKeys = [ { outPath = "${./initrd-network-ssh/ssh_host_ed25519_key}"; } ]; + }; + }; + }; + }; + + client = { config, ... }: { + environment.etc = { + knownHosts = { + text = concatStrings [ + "server," + "${ + toString (head (splitString " " (toString + (elemAt (splitString "\n" config.networking.extraHosts) 2)))) + } " + "${readFile ./initrd-network-ssh/ssh_host_ed25519_key.pub}" + ]; + }; + sshKey = { + source = ./initrd-network-ssh/id_ed25519; + mode = "0600"; + }; + }; + }; + }; + + testScript = '' + start_all() + + def ssh_is_up(_) -> bool: + status, _ = client.execute("nc -z server 22") + return status == 0 + + server.wait_for_unit("multi-user.target") + server.succeed( + "echo somepass | cryptsetup luksFormat --type=luks2 /dev/vdc", + "bootctl set-default nixos-generation-1-specialisation-encrypted-root.conf", + "sync", + ) + server.shutdown() + server.start() + + client.wait_for_unit("network.target") + with client.nested("waiting for SSH server to come up"): + retry(ssh_is_up) + + client.succeed( + "echo somepass | ssh -i /etc/sshKey -o UserKnownHostsFile=/etc/knownHosts server 'systemd-tty-ask-password-agent' & exit" + ) + + server.wait_for_unit("multi-user.target") + server.succeed("mount | grep '/dev/mapper/root on /'") + ''; +}) -- cgit 1.4.1 From 834ec135ce71cdc93aba9e98cf67f42e41502f32 Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Fri, 17 Feb 2023 07:47:40 -0500 Subject: systemd-initrd: OpenVPN --- nixos/modules/system/boot/initrd-openvpn.nix | 21 ++++++++++++++++++--- nixos/tests/all-tests.nix | 1 + nixos/tests/initrd-network-openvpn/default.nix | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) (limited to 'nixos') diff --git a/nixos/modules/system/boot/initrd-openvpn.nix b/nixos/modules/system/boot/initrd-openvpn.nix index cbc61d55d6bb3..2530240628e42 100644 --- a/nixos/modules/system/boot/initrd-openvpn.nix +++ b/nixos/modules/system/boot/initrd-openvpn.nix @@ -51,7 +51,7 @@ in # Add openvpn and ip binaries to the initrd # The shared libraries are required for DNS resolution - boot.initrd.extraUtilsCommands = '' + boot.initrd.extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable) '' copy_bin_and_libs ${pkgs.openvpn}/bin/openvpn copy_bin_and_libs ${pkgs.iproute2}/bin/ip @@ -59,18 +59,33 @@ in cp -pv ${pkgs.glibc}/lib/libnss_dns.so.2 $out/lib ''; + boot.initrd.systemd.storePaths = [ + "${pkgs.openvpn}/bin/openvpn" + "${pkgs.iproute2}/bin/ip" + "${pkgs.glibc}/lib/libresolv.so.2" + "${pkgs.glibc}/lib/libnss_dns.so.2" + ]; + boot.initrd.secrets = { "/etc/initrd.ovpn" = cfg.configuration; }; # openvpn --version would exit with 1 instead of 0 - boot.initrd.extraUtilsCommandsTest = '' + boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) '' $out/bin/openvpn --show-gateway ''; - boot.initrd.network.postCommands = '' + boot.initrd.network.postCommands = mkIf (!config.boot.initrd.systemd.enable) '' openvpn /etc/initrd.ovpn & ''; + + boot.initrd.systemd.services.openvpn = { + wantedBy = [ "initrd.target" ]; + path = [ pkgs.iproute2 ]; + after = [ "network.target" "initrd-nixos-copy-secrets.service" ]; + serviceConfig.ExecStart = "${pkgs.openvpn}/bin/openvpn /etc/initrd.ovpn"; + serviceConfig.Type = "notify"; + }; }; } diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 5771d1c3bc96d..95b67617fe971 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -679,6 +679,7 @@ in { systemd-initrd-vconsole = handleTest ./systemd-initrd-vconsole.nix {}; systemd-initrd-networkd = handleTest ./systemd-initrd-networkd.nix {}; systemd-initrd-networkd-ssh = handleTest ./systemd-initrd-networkd-ssh.nix {}; + systemd-initrd-networkd-openvpn = handleTest ./initrd-network-openvpn { systemdStage1 = true; }; systemd-journal = handleTest ./systemd-journal.nix {}; systemd-machinectl = handleTest ./systemd-machinectl.nix {}; systemd-networkd = handleTest ./systemd-networkd.nix {}; diff --git a/nixos/tests/initrd-network-openvpn/default.nix b/nixos/tests/initrd-network-openvpn/default.nix index 1150e2dc5b0d2..769049905eb8c 100644 --- a/nixos/tests/initrd-network-openvpn/default.nix +++ b/nixos/tests/initrd-network-openvpn/default.nix @@ -1,3 +1,9 @@ +{ system ? builtins.currentSystem +, config ? {} +, pkgs ? import ../.. { inherit system config; } +, systemdStage1 ? false +}: + import ../make-test-python.nix ({ lib, ...}: { @@ -22,6 +28,7 @@ import ../make-test-python.nix ({ lib, ...}: minimalboot = { ... }: { + boot.initrd.systemd.enable = systemdStage1; boot.initrd.network = { enable = true; openvpn = { @@ -39,6 +46,17 @@ import ../make-test-python.nix ({ lib, ...}: virtualisation.vlans = [ 1 ]; boot.initrd = { + systemd.enable = systemdStage1; + systemd.extraBin.nc = "${pkgs.busybox}/bin/nc"; + systemd.services.nc = { + requiredBy = ["initrd.target"]; + after = ["network.target"]; + serviceConfig = { + ExecStart = "/bin/nc -p 1234 -lke /bin/echo TESTVALUE"; + Type = "oneshot"; + }; + }; + # This command does not fork to keep the VM in the state where # only the initramfs is loaded preLVMCommands = -- cgit 1.4.1 From 85982346510230f7bfe1aeabfa2f9382c8192ad8 Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Mon, 6 Mar 2023 22:21:48 -0500 Subject: systemd-initrd: dbus --- nixos/modules/services/system/dbus.nix | 21 ++++++++++++++++++++- nixos/modules/system/boot/networkd.nix | 3 +++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'nixos') diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix index c677088101f0c..9d8a62ec78c53 100644 --- a/nixos/modules/services/system/dbus.nix +++ b/nixos/modules/services/system/dbus.nix @@ -14,13 +14,17 @@ let serviceDirectories = cfg.packages; }; - inherit (lib) mkOption mkIf mkMerge types; + inherit (lib) mkOption mkEnableOption mkIf mkMerge types; in { options = { + boot.initrd.systemd.dbus = { + enable = mkEnableOption (lib.mdDoc "dbus in stage 1") // { visible = false; }; + }; + services.dbus = { enable = mkOption { @@ -111,6 +115,21 @@ in ]; } + (mkIf config.boot.initrd.systemd.dbus.enable { + boot.initrd.systemd = { + users.messagebus = { }; + groups.messagebus = { }; + contents."/etc/dbus-1".source = pkgs.makeDBusConf { + inherit (cfg) apparmor; + suidHelper = "/bin/false"; + serviceDirectories = [ pkgs.dbus ]; + }; + packages = [ pkgs.dbus ]; + storePaths = [ "${pkgs.dbus}/bin/dbus-daemon" ]; + targets.sockets.wants = [ "dbus.socket" ]; + }; + }) + (mkIf (cfg.implementation == "dbus") { environment.systemPackages = [ pkgs.dbus diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index e325a33a9e377..d2a8a28c4e695 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -3155,6 +3155,9 @@ let systemd.package = pkgs.systemdStage1Network; + # For networkctl + systemd.dbus.enable = mkDefault true; + systemd.additionalUpstreamUnits = [ "systemd-networkd-wait-online.service" "systemd-networkd.service" -- cgit 1.4.1 From 3cb9534df64937f3425dbc0f63749cc83276791a Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Mon, 6 Mar 2023 13:46:17 -0500 Subject: systemd-initrd: Flush networkd --- nixos/modules/system/boot/initrd-network.nix | 6 ++++- nixos/modules/system/boot/networkd.nix | 33 +++++++++++++++++++++++++++ nixos/tests/systemd-initrd-networkd.nix | 34 +++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 2 deletions(-) (limited to 'nixos') diff --git a/nixos/modules/system/boot/initrd-network.nix b/nixos/modules/system/boot/initrd-network.nix index a1017c3e24204..e8bbf1d040329 100644 --- a/nixos/modules/system/boot/initrd-network.nix +++ b/nixos/modules/system/boot/initrd-network.nix @@ -67,11 +67,15 @@ in boot.initrd.network.flushBeforeStage2 = mkOption { type = types.bool; - default = true; + default = !config.boot.initrd.systemd.enable; + defaultText = "!config.boot.initrd.systemd.enable"; description = lib.mdDoc '' Whether to clear the configuration of the interfaces that were set up in the initrd right before stage 2 takes over. Stage 2 will do the regular network configuration based on the NixOS networking options. + + The default is false when systemd is enabled in initrd, + because the systemd-networkd documentation suggests it. ''; }; diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index d2a8a28c4e695..bd2f1cc4374a7 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -3186,6 +3186,39 @@ let ]; kernelModules = [ "af_packet" ]; + systemd.services.nixos-flush-networkd = mkIf config.boot.initrd.network.flushBeforeStage2 { + description = "Flush Network Configuration"; + wantedBy = ["initrd.target"]; + after = ["systemd-networkd.service" "dbus.socket" "dbus.service"]; + before = ["shutdown.target" "initrd-switch-root.target"]; + conflicts = ["shutdown.target" "initrd-switch-root.target"]; + unitConfig.DefaultDependencies = false; + serviceConfig = { + # This service does nothing when starting, but brings down + # interfaces when switching root. This is the easiest way to + # ensure proper ordering while stopping. See systemd.unit(5) + # section on Before= and After=. The important part is that + # we are stopped before units we need, like dbus.service, + # and that we are stopped before starting units like + # initrd-switch-root.target + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "/bin/true"; + }; + # systemd-networkd doesn't bring down interfaces on its own + # when it exits (see: systemd-networkd(8)), so we have to do + # it ourselves. The networkctl command doesn't have a way to + # bring all interfaces down, so we have to iterate over the + # list and filter out unmanaged interfaces to bring them down + # individually. + preStop = '' + networkctl list --full --no-legend | while read _idx link _type _operational setup _; do + [ "$setup" = unmanaged ] && continue + networkctl down "$link" + done + ''; + }; + }) ]; diff --git a/nixos/tests/systemd-initrd-networkd.nix b/nixos/tests/systemd-initrd-networkd.nix index 1656f5d5e1898..00ecbec5613c4 100644 --- a/nixos/tests/systemd-initrd-networkd.nix +++ b/nixos/tests/systemd-initrd-networkd.nix @@ -2,7 +2,23 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { name = "systemd-initrd-network"; meta.maintainers = [ lib.maintainers.elvishjerricco ]; - nodes = { + nodes = let + mkFlushTest = flush: script: { ... }: { + boot.initrd.systemd.enable = true; + boot.initrd.network = { + enable = true; + flushBeforeStage2 = flush; + }; + systemd.services.check-flush = { + requiredBy = ["multi-user.target"]; + before = ["network-pre.target" "multi-user.target"]; + unitConfig.DefaultDependencies = false; + serviceConfig.Type = "oneshot"; + path = [ pkgs.iproute2 pkgs.iputils pkgs.gnugrep ]; + inherit script; + }; + }; + in { basic = { ... }: { boot.initrd.network.enable = true; @@ -29,11 +45,27 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: { }; }; }; + + doFlush = mkFlushTest true '' + if ip addr | grep 10.0.2.15; then + echo "Network configuration survived switch-root; flushBeforeStage2 failed" + exit 1 + fi + ''; + + dontFlush = mkFlushTest false '' + if ! (ip addr | grep 10.0.2.15); then + echo "Network configuration didn't survive switch-root" + exit 1 + fi + ''; }; testScript = '' start_all() basic.wait_for_unit("multi-user.target") + doFlush.wait_for_unit("multi-user.target") + dontFlush.wait_for_unit("multi-user.target") # Make sure the systemd-network user was set correctly in initrd basic.succeed("[ $(stat -c '%U,%G' /run/systemd/netif/links) = systemd-network,systemd-network ]") basic.succeed("ip addr show >&2") -- cgit 1.4.1 From a4807f566dab6aeb85e72aeb66a7006336148cfd Mon Sep 17 00:00:00 2001 From: Francesco Gazzetta Date: Fri, 21 Apr 2023 15:35:00 +0200 Subject: nixosTests.ft2-clone: make OCR more robust --- nixos/tests/ft2-clone.nix | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'nixos') diff --git a/nixos/tests/ft2-clone.nix b/nixos/tests/ft2-clone.nix index 3c90b3d3fa201..a8395d4ebaa62 100644 --- a/nixos/tests/ft2-clone.nix +++ b/nixos/tests/ft2-clone.nix @@ -26,9 +26,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { machine.wait_for_window(r"Fasttracker") machine.sleep(5) - # One of the few words that actually get recognized - if "Songlen" not in machine.get_screen_text(): - raise Exception("Program did not start successfully") + machine.wait_for_text(r"(Songlen|Repstart|Time|About|Nibbles|Help)") machine.screenshot("screen") ''; }) -- cgit 1.4.1 From e25dc4a95ed69f37ce443b8fcad00fb9337e6eed Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 21 Apr 2023 17:18:45 +0200 Subject: nixos/nginx: Fix listen string generation This regressed in 327b0cff7aedc20a148d245b1182f43800acc1f5 breaking nixosTests.php, among other things. --- nixos/modules/services/web-servers/nginx/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nixos') diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 4f97fe752ef70..1e6cb0d374053 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -318,7 +318,7 @@ let listenString = { addr, port, ssl, extraParameters ? [], ... }: # UDP listener for QUIC transport protocol. - (optionalString (ssl && vhost.quic) " + (optionalString (ssl && vhost.quic) (" listen ${addr}:${toString port} quic " + optionalString vhost.default "default_server " + optionalString vhost.reuseport "reuseport " @@ -326,7 +326,7 @@ let let inCompatibleParameters = [ "ssl" "proxy_protocol" "http2" ]; isCompatibleParameter = param: !(any (p: p == param) inCompatibleParameters); in filter isCompatibleParameter extraParameters)) - + ";") + + ";")) + " listen ${addr}:${toString port} " -- cgit 1.4.1