summary refs log tree commit diff
path: root/nixos/modules/config
diff options
context:
space:
mode:
authorJanne Heß <janne@hess.ooo>2021-09-03 17:21:36 +0200
committerJanne Heß <janne@hess.ooo>2021-09-07 10:30:42 +0200
commita851b4d20ee0dad33395f85a092df47ac9b1cc3e (patch)
treef865c0ea3d15711bdb821f04dc818b9e1703ad71 /nixos/modules/config
parent315673040200723568c562b68550a2f8b4fe6e6a (diff)
downloadnixpkgs-a851b4d20ee0dad33395f85a092df47ac9b1cc3e.tar
nixpkgs-a851b4d20ee0dad33395f85a092df47ac9b1cc3e.tar.gz
nixpkgs-a851b4d20ee0dad33395f85a092df47ac9b1cc3e.tar.bz2
nixpkgs-a851b4d20ee0dad33395f85a092df47ac9b1cc3e.tar.lz
nixpkgs-a851b4d20ee0dad33395f85a092df47ac9b1cc3e.tar.xz
nixpkgs-a851b4d20ee0dad33395f85a092df47ac9b1cc3e.tar.zst
nixpkgs-a851b4d20ee0dad33395f85a092df47ac9b1cc3e.zip
nixos/users-groups: Add dry mode
Diffstat (limited to 'nixos/modules/config')
-rw-r--r--nixos/modules/config/update-users-groups.pl43
-rw-r--r--nixos/modules/config/users-groups.nix6
2 files changed, 32 insertions, 17 deletions
diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl
index bef08dc4020..232f886789d 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 f86be3be2c6..d88162558e6 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" ] "";