Merge pull request #169116 from ElvishJerricco/systemd-stage-1-networkd
Systemd stage 1 networkd
This commit is contained in:
commit
6b27ed3229
20 changed files with 715 additions and 224 deletions
|
@ -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
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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,116 @@ 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;
|
||||
|
||||
# For networkctl
|
||||
systemd.dbus.enable = mkDefault true;
|
||||
|
||||
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" ];
|
||||
|
||||
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
|
||||
'';
|
||||
};
|
||||
|
||||
})
|
||||
];
|
||||
|
||||
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;
|
||||
})
|
||||
];
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
'';
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 = [
|
||||
|
@ -378,7 +369,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)}
|
||||
'';
|
||||
|
@ -388,8 +379,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";
|
||||
|
|
|
@ -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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
];
|
||||
}
|
||||
|
|
|
@ -869,6 +869,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.
|
||||
|
|
|
@ -680,6 +680,9 @@ 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-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 {};
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
{ system ? builtins.currentSystem
|
||||
, config ? {}
|
||||
, pkgs ? import ../.. { inherit system config; }
|
||||
, systemdStage1 ? false
|
||||
}:
|
||||
|
||||
import ../make-test-python.nix ({ lib, ...}:
|
||||
|
||||
{
|
||||
|
@ -22,11 +28,12 @@ import ../make-test-python.nix ({ lib, ...}:
|
|||
minimalboot =
|
||||
{ ... }:
|
||||
{
|
||||
boot.initrd.systemd.enable = systemdStage1;
|
||||
boot.initrd.network = {
|
||||
enable = true;
|
||||
openvpn = {
|
||||
enable = true;
|
||||
configuration = "/dev/null";
|
||||
configuration = builtins.toFile "initrd.ovpn" "";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = ''
|
||||
|
|
82
nixos/tests/systemd-initrd-networkd-ssh.nix
Normal file
82
nixos/tests/systemd-initrd-networkd-ssh.nix
Normal file
|
@ -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 /'")
|
||||
'';
|
||||
})
|
74
nixos/tests/systemd-initrd-networkd.nix
Normal file
74
nixos/tests/systemd-initrd-networkd.nix
Normal file
|
@ -0,0 +1,74 @@
|
|||
import ./make-test-python.nix ({ pkgs, lib, ... }: {
|
||||
name = "systemd-initrd-network";
|
||||
meta.maintainers = [ lib.maintainers.elvishjerricco ];
|
||||
|
||||
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;
|
||||
|
||||
boot.initrd.systemd = {
|
||||
enable = true;
|
||||
# Enable network-online to fail the test in case of timeout
|
||||
network.wait-online.timeout = 10;
|
||||
network.wait-online.anyInterface = true;
|
||||
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
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
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")
|
||||
basic.succeed("ip route show >&2")
|
||||
'';
|
||||
})
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue