diff options
48 files changed, 630 insertions, 172 deletions
diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index 35ccd0b1d983..ea8e4f660a7f 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -1520,12 +1520,6 @@ githubId = 355401; name = "Brian Hicks"; }; - bricewge = { - email = "bricewge@gmail.com"; - github = "bricewge"; - githubId = 5525646; - name = "Brice Waegeneire"; - }; Br1ght0ne = { email = "brightone@protonmail.com"; github = "Br1ght0ne"; diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl index bef08dc40207..232f886789d3 100644 --- a/nixos/modules/config/update-users-groups.pl +++ b/nixos/modules/config/update-users-groups.pl @@ -1,11 +1,10 @@ use strict; +use warnings; use File::Path qw(make_path); use File::Slurp; +use Getopt::Long; use JSON; -make_path("/var/lib/nixos", { mode => 0755 }); - - # Keep track of deleted uids and gids. my $uidMapFile = "/var/lib/nixos/uid-map"; my $uidMap = -e $uidMapFile ? decode_json(read_file($uidMapFile)) : {}; @@ -13,12 +12,19 @@ my $uidMap = -e $uidMapFile ? decode_json(read_file($uidMapFile)) : {}; my $gidMapFile = "/var/lib/nixos/gid-map"; my $gidMap = -e $gidMapFile ? decode_json(read_file($gidMapFile)) : {}; +my $is_dry = ($ENV{'NIXOS_ACTION'} // "") eq "dry-activate"; +GetOptions("dry-activate" => \$is_dry); +make_path("/var/lib/nixos", { mode => 0755 }) unless $is_dry; sub updateFile { my ($path, $contents, $perms) = @_; + return if $is_dry; write_file($path, { atomic => 1, binmode => ':utf8', perms => $perms // 0644 }, $contents) or die; } +sub nscdInvalidate { + system("nscd", "--invalidate", $_[0]) unless $is_dry; +} sub hashPassword { my ($password) = @_; @@ -28,6 +34,14 @@ sub hashPassword { return crypt($password, '$6$' . $salt . '$'); } +sub dry_print { + if ($is_dry) { + print STDERR ("$_[1] $_[2]\n") + } else { + print STDERR ("$_[0] $_[2]\n") + } +} + # Functions for allocating free GIDs/UIDs. FIXME: respect ID ranges in # /etc/login.defs. @@ -51,7 +65,7 @@ sub allocGid { my ($name) = @_; my $prevGid = $gidMap->{$name}; if (defined $prevGid && !defined $gidsUsed{$prevGid}) { - print STDERR "reviving group '$name' with GID $prevGid\n"; + dry_print("reviving", "would revive", "group '$name' with GID $prevGid"); $gidsUsed{$prevGid} = 1; return $prevGid; } @@ -63,15 +77,14 @@ sub allocUid { my ($min, $max, $up) = $isSystemUser ? (400, 999, 0) : (1000, 29999, 1); my $prevUid = $uidMap->{$name}; if (defined $prevUid && $prevUid >= $min && $prevUid <= $max && !defined $uidsUsed{$prevUid}) { - print STDERR "reviving user '$name' with UID $prevUid\n"; + dry_print("reviving", "would revive", "user '$name' with UID $prevUid"); $uidsUsed{$prevUid} = 1; return $prevUid; } return allocId(\%uidsUsed, \%uidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) }); } - -# Read the declared users/groups. +# Read the declared users/groups my $spec = decode_json(read_file($ARGV[0])); # Don't allocate UIDs/GIDs that are manually assigned. @@ -134,7 +147,7 @@ foreach my $g (@{$spec->{groups}}) { if (defined $existing) { $g->{gid} = $existing->{gid} if !defined $g->{gid}; if ($g->{gid} != $existing->{gid}) { - warn "warning: not applying GID change of group ‘$name’ ($existing->{gid} -> $g->{gid})\n"; + dry_print("warning: not applying", "warning: would not apply", "GID change of group ‘$name’ ($existing->{gid} -> $g->{gid})"); $g->{gid} = $existing->{gid}; } $g->{password} = $existing->{password}; # do we want this? @@ -163,7 +176,7 @@ foreach my $name (keys %groupsCur) { my $g = $groupsCur{$name}; next if defined $groupsOut{$name}; if (!$spec->{mutableUsers} || defined $declGroups{$name}) { - print STDERR "removing group ‘$name’\n"; + dry_print("removing group", "would remove group", "‘$name’"); } else { $groupsOut{$name} = $g; } @@ -175,7 +188,7 @@ my @lines = map { join(":", $_->{name}, $_->{password}, $_->{gid}, $_->{members} (sort { $a->{gid} <=> $b->{gid} } values(%groupsOut)); updateFile($gidMapFile, to_json($gidMap)); updateFile("/etc/group", \@lines); -system("nscd --invalidate group"); +nscdInvalidate("group"); # Generate a new /etc/passwd containing the declared users. my %usersOut; @@ -196,7 +209,7 @@ foreach my $u (@{$spec->{users}}) { if (defined $existing) { $u->{uid} = $existing->{uid} if !defined $u->{uid}; if ($u->{uid} != $existing->{uid}) { - warn "warning: not applying UID change of user ‘$name’ ($existing->{uid} -> $u->{uid})\n"; + dry_print("warning: not applying", "warning: would not apply", "UID change of user ‘$name’ ($existing->{uid} -> $u->{uid})"); $u->{uid} = $existing->{uid}; } } else { @@ -211,7 +224,7 @@ foreach my $u (@{$spec->{users}}) { # Ensure home directory incl. ownership and permissions. if ($u->{createHome}) { - make_path($u->{home}, { mode => 0700 }) if ! -e $u->{home}; + make_path($u->{home}, { mode => 0700 }) if ! -e $u->{home} and ! $is_dry; chown $u->{uid}, $u->{gid}, $u->{home}; chmod 0700, $u->{home}; } @@ -250,7 +263,7 @@ foreach my $name (keys %usersCur) { my $u = $usersCur{$name}; next if defined $usersOut{$name}; if (!$spec->{mutableUsers} || defined $declUsers{$name}) { - print STDERR "removing user ‘$name’\n"; + dry_print("removing user", "would remove user", "‘$name’"); } else { $usersOut{$name} = $u; } @@ -261,7 +274,7 @@ foreach my $name (keys %usersCur) { (sort { $a->{uid} <=> $b->{uid} } (values %usersOut)); updateFile($uidMapFile, to_json($uidMap)); updateFile("/etc/passwd", \@lines); -system("nscd --invalidate passwd"); +nscdInvalidate("passwd"); # Rewrite /etc/shadow to add new accounts or remove dead ones. @@ -293,7 +306,7 @@ updateFile("/etc/shadow", \@shadowNew, 0640); my $uid = getpwnam "root"; my $gid = getgrnam "shadow"; my $path = "/etc/shadow"; - chown($uid, $gid, $path) || die "Failed to change ownership of $path: $!"; + (chown($uid, $gid, $path) || die "Failed to change ownership of $path: $!") unless $is_dry; } # Rewrite /etc/subuid & /etc/subgid to include default container mappings diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix index f86be3be2c65..d88162558e66 100644 --- a/nixos/modules/config/users-groups.nix +++ b/nixos/modules/config/users-groups.nix @@ -561,14 +561,16 @@ in { shadow.gid = ids.gids.shadow; }; - system.activationScripts.users = stringAfter [ "stdio" ] - '' + system.activationScripts.users = { + supportsDryActivation = true; + text = '' install -m 0700 -d /root install -m 0755 -d /home ${pkgs.perl.withPackages (p: [ p.FileSlurp p.JSON ])}/bin/perl \ -w ${./update-users-groups.pl} ${spec} ''; + }; # for backwards compatibility system.activationScripts.groups = stringAfter [ "users" ] ""; diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index 3a6930314b1a..548b4de852b7 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -17,6 +17,41 @@ let ''; }); + systemActivationScript = set: onlyDry: let + set' = filterAttrs (_: v: onlyDry -> v.supportsDryActivation) (mapAttrs (_: v: if isString v then (noDepEntry v) // { supportsDryActivation = false; } else v) set); + withHeadlines = addAttributeName set'; + in + '' + #!${pkgs.runtimeShell} + + systemConfig='@out@' + + export PATH=/empty + for i in ${toString path}; do + PATH=$PATH:$i/bin:$i/sbin + done + + _status=0 + trap "_status=1 _localstatus=\$?" ERR + + # Ensure a consistent umask. + umask 0022 + + ${textClosureMap id (withHeadlines) (attrNames withHeadlines)} + + '' + optionalString (!onlyDry) '' + # Make this configuration the current configuration. + # The readlink is there to ensure that when $systemConfig = /system + # (which is a symlink to the store), /run/current-system is still + # used as a garbage collection root. + ln -sfn "$(readlink -f "$systemConfig")" /run/current-system + + # Prevent the current configuration from being garbage-collected. + ln -sfn /run/current-system /nix/var/nix/gcroots/current-system + + exit $_status + ''; + path = with pkgs; map getBin [ coreutils gnugrep @@ -28,7 +63,7 @@ let util-linux # needed for mount and mountpoint ]; - scriptType = with types; + scriptType = withDry: with types; let scriptOptions = { deps = mkOption { type = types.listOf types.str; @@ -39,6 +74,19 @@ let { type = types.lines; description = "The content of the script."; }; + } // optionalAttrs withDry { + supportsDryActivation = mkOption + { type = types.bool; + default = false; + description = '' + Whether this activation script supports being dry-activated. + These activation scripts will also be executed on dry-activate + activations with the environment variable + <literal>NIXOS_ACTION</literal> being set to <literal>dry-activate + </literal>. it's important that these activation scripts don't + modify anything about the system when the variable is set. + ''; + }; }; in either str (submodule { options = scriptOptions; }); @@ -74,47 +122,19 @@ in idempotent and fast. ''; - type = types.attrsOf scriptType; - - apply = set: { - script = - '' - #! ${pkgs.runtimeShell} - - systemConfig=@out@ - - export PATH=/empty - for i in ${toString path}; do - PATH=$PATH:$i/bin:$i/sbin - done - - _status=0 - trap "_status=1 _localstatus=\$?" ERR - - # Ensure a consistent umask. - umask 0022 - - ${ - let - set' = mapAttrs (n: v: if isString v then noDepEntry v else v) set; - withHeadlines = addAttributeName set'; - in textClosureMap id (withHeadlines) (attrNames withHeadlines) - } - - # Make this configuration the current configuration. - # The readlink is there to ensure that when $systemConfig = /system - # (which is a symlink to the store), /run/current-system is still - # used as a garbage collection root. - ln -sfn "$(readlink -f "$systemConfig")" /run/current-system - - # Prevent the current configuration from being garbage-collected. - ln -sfn /run/current-system /nix/var/nix/gcroots/current-system - - exit $_status - ''; + type = types.attrsOf (scriptType true); + apply = set: set // { + script = systemActivationScript set false; }; }; + system.dryActivationScript = mkOption { + description = "The shell script that is to be run when dry-activating a system."; + readOnly = true; + internal = true; + default = systemActivationScript (removeAttrs config.system.activationScripts [ "script" ]) true; + }; + system.userActivationScripts = mkOption { default = {}; @@ -137,7 +157,7 @@ in idempotent and fast. ''; - type = with types; attrsOf scriptType; + type = with types; attrsOf (scriptType false); apply = set: { script = '' diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl index dd391c8b5d78..b7a062755296 100644 --- a/nixos/modules/system/activation/switch-to-configuration.pl +++ b/nixos/modules/system/activation/switch-to-configuration.pl @@ -36,6 +36,8 @@ EOF exit 1; } +$ENV{NIXOS_ACTION} = $action; + # This is a NixOS installation if it has /etc/NIXOS or a proper # /etc/os-release. die "This is not a NixOS installation!\n" unless @@ -360,6 +362,10 @@ if ($action eq "dry-activate") { if scalar @unitsToStopFiltered > 0; print STDERR "would NOT stop the following changed units: ", join(", ", sort(keys %unitsToSkip)), "\n" if scalar(keys %unitsToSkip) > 0; + + print STDERR "would activate the configuration...\n"; + system("$out/dry-activate", "$out"); + print STDERR "would restart systemd\n" if $restartSystemd; print STDERR "would restart the following units: ", join(", ", sort(keys %unitsToRestart)), "\n" if scalar(keys %unitsToRestart) > 0; diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index d3e4923a993f..80835d9688f2 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -56,9 +56,11 @@ let ''} echo "$activationScript" > $out/activate + echo "$dryActivationScript" > $out/dry-activate substituteInPlace $out/activate --subst-var out - chmod u+x $out/activate - unset activationScript + substituteInPlace $out/dry-activate --subst-var out + chmod u+x $out/activate $out/dry-activate + unset activationScript dryActivationScript cp ${config.system.build.bootStage2} $out/init substituteInPlace $out/init --subst-var-by systemConfig $out @@ -108,6 +110,7 @@ let config.system.build.installBootLoader or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true"; activationScript = config.system.activationScripts.script; + dryActivationScript = config.system.dryActivationScript; nixosLabel = config.system.nixos.label; configurationName = config.boot.loader.grub.configurationName; diff --git a/nixos/modules/tasks/lvm.nix b/nixos/modules/tasks/lvm.nix index 98a0e2ddef90..aaa76b49fa30 100644 --- a/nixos/modules/tasks/lvm.nix +++ b/nixos/modules/tasks/lvm.nix @@ -46,22 +46,32 @@ in { kernelModules = [ "dm-snapshot" "dm-thin-pool" ]; extraUtilsCommands = '' - copy_bin_and_libs ${pkgs.thin-provisioning-tools}/bin/pdata_tools - copy_bin_and_libs ${pkgs.thin-provisioning-tools}/bin/thin_check + for BIN in ${pkgs.thin-provisioning-tools}/bin/*; do + copy_bin_and_libs $BIN + done + ''; + + extraUtilsCommandsTest = '' + ls ${pkgs.thin-provisioning-tools}/bin/ | grep -v pdata_tools | while read BIN; do + $out/bin/$(basename $BIN) --help > /dev/null + done ''; }; - environment.etc."lvm/lvm.conf".text = '' - global/thin_check_executable = "${pkgs.thin-provisioning-tools}/bin/thin_check" - ''; + environment.etc."lvm/lvm.conf".text = concatMapStringsSep "\n" + (bin: "global/${bin}_executable = ${pkgs.thin-provisioning-tools}/bin/${bin}") + [ "thin_check" "thin_dump" "thin_repair" "cache_check" "cache_dump" "cache_repair" ]; }) (mkIf (cfg.dmeventd.enable || cfg.boot.thin.enable) { boot.initrd.preLVMCommands = '' mkdir -p /etc/lvm cat << EOF >> /etc/lvm/lvm.conf - ${optionalString cfg.boot.thin.enable '' - global/thin_check_executable = "$(command -v thin_check)" - ''} + ${optionalString cfg.boot.thin.enable ( + concatMapStringsSep "\n" + (bin: "global/${bin}_executable = $(command -v ${bin})") + [ "thin_check" "thin_dump" "thin_repair" "cache_check" "cache_dump" "cache_repair" ] + ) + } ${optionalString cfg.dmeventd.enable '' dmeventd/executable = "$(command -v false)" activation/monitoring = 0 diff --git a/nixos/tests/miniflux.nix b/nixos/tests/miniflux.nix index 9a25a9e77cc9..1015550fa8c7 100644 --- a/nixos/tests/miniflux.nix +++ b/nixos/tests/miniflux.nix @@ -11,7 +11,7 @@ in with lib; { name = "miniflux"; - meta.maintainers = with pkgs.lib.maintainers; [ bricewge ]; + meta.maintainers = with pkgs.lib.maintainers; [ ]; nodes = { default = diff --git a/nixos/tests/mutable-users.nix b/nixos/tests/mutable-users.nix index e3f002d9b198..ebe32e6487ef 100644 --- a/nixos/tests/mutable-users.nix +++ b/nixos/tests/mutable-users.nix @@ -12,6 +12,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { }; mutable = { ... }: { users.mutableUsers = true; + users.users.dry-test.isNormalUser = true; }; }; @@ -41,5 +42,32 @@ import ./make-test-python.nix ({ pkgs, ...} : { "${mutableSystem}/bin/switch-to-configuration test" ) assert "/run/wrappers/" in machine.succeed("which passwd") + + with subtest("dry-activation does not change files"): + machine.succeed('test -e /home/dry-test') # home was created + machine.succeed('rm -rf /home/dry-test') + + files_to_check = ['/etc/group', + '/etc/passwd', + '/etc/shadow', + '/etc/subuid', + '/etc/subgid', + '/var/lib/nixos/uid-map', + '/var/lib/nixos/gid-map', + '/var/lib/nixos/declarative-groups', + '/var/lib/nixos/declarative-users' + ] + expected_hashes = {} + expected_stats = {} + for file in files_to_check: + expected_hashes[file] = machine.succeed(f"sha256sum {file}") + expected_stats[file] = machine.succeed(f"stat {file}") + + machine.succeed("/run/current-system/bin/switch-to-configuration dry-activate") + + machine.fail('test -e /home/dry-test') # home was not recreated + for file in files_to_check: + assert machine.succeed(f"sha256sum {file}") == expected_hashes[file] + assert machine.succeed(f"stat {file}") == expected_stats[file] ''; }) diff --git a/pkgs/applications/audio/lollypop/default.nix b/pkgs/applications/audio/lollypop/default.nix index 860a621847da..564277562885 100644 --- a/pkgs/applications/audio/lollypop/default.nix +++ b/pkgs/applications/audio/lollypop/default.nix @@ -25,7 +25,7 @@ python3.pkgs.buildPythonApplication rec { pname = "lollypop"; - version = "1.4.17"; + version = "1.4.23"; format = "other"; doCheck = false; @@ -34,7 +34,7 @@ python3.pkgs.buildPythonApplication rec { url = "https://gitlab.gnome.org/World/lollypop"; rev = "refs/tags/${version}"; fetchSubmodules = true; - sha256 = "sha256-GrznUXIYUTYOKQ1znsCqmBdm5YImCABMK2NGRtx5fSk="; + sha256 = "sha256-wwdH3gMpYt40VGqrL1XfB1dOfg45zLKtTEI23AwjCis="; }; nativeBuildInputs = [ diff --git a/pkgs/applications/virtualization/virtualbox/default.nix b/pkgs/applications/virtualization/virtualbox/default.nix index c693fa8fdcf1..f54f9d84546b 100644 --- a/pkgs/applications/virtualization/virtualbox/default.nix +++ b/pkgs/applications/virtualization/virtualbox/default.nix @@ -200,6 +200,11 @@ in stdenv.mkDerivation { done ''} + # https://github.com/NixOS/nixpkgs/issues/137104 + ${optionalString (enableHardening || headless) '' + rm $libexec/components/VBoxREM.so + ''} + cp -rv out/linux.*/${buildType}/bin/src "$modsrc" ''; diff --git a/pkgs/build-support/mkshell/default.nix b/pkgs/build-support/mkshell/default.nix index f85a11394cb5..27ee7e322624 100644 --- a/pkgs/build-support/mkshell/default.nix +++ b/ |