diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
index 688671efffc..97b94c5756a 100644
--- a/nixos/doc/manual/release-notes/rl-2009.xml
+++ b/nixos/doc/manual/release-notes/rl-2009.xml
@@ -124,6 +124,12 @@ systemd.services.mysql.serviceConfig.ReadWritePaths = [ "/var/data" ];
services.postfix.sslCACert was replaced by services.postfix.tlsTrustedAuthorities which now defaults to system certifcate authorities.
+
+
+ Subordinate GID and UID mappings are now set up automatically for all normal users.
+ This will make container tools like Podman work as non-root users out of the box.
+
+
diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl
index 15e448b787a..e1c7a46e430 100644
--- a/nixos/modules/config/update-users-groups.pl
+++ b/nixos/modules/config/update-users-groups.pl
@@ -281,3 +281,58 @@ foreach my $u (values %usersOut) {
}
updateFile("/etc/shadow", \@shadowNew, 0600);
+
+# Rewrite /etc/subuid & /etc/subgid to include default container mappings
+
+my $subUidMapFile = "/var/lib/nixos/auto-subuid-map";
+my $subUidMap = -e $subUidMapFile ? decode_json(read_file($subUidMapFile)) : {};
+
+my (%subUidsUsed, %subUidsPrevUsed);
+
+$subUidsPrevUsed{$_} = 1 foreach values %{$subUidMap};
+
+sub allocSubUid {
+ my ($name, @rest) = @_;
+
+ # TODO: No upper bounds?
+ my ($min, $max, $up) = (100000, 100000 * 100, 1);
+ my $prevId = $subUidMap->{$name};
+ if (defined $prevId && !defined $subUidsUsed{$prevId}) {
+ $subUidsUsed{$prevId} = 1;
+ return $prevId;
+ }
+
+ my $id = allocId(\%subUidsUsed, \%subUidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
+ my $offset = $id - 100000;
+ my $count = $offset * 65536;
+ my $subordinate = 100000 + $count;
+ return $subordinate;
+}
+
+my @subGids;
+my @subUids;
+foreach my $u (values %usersOut) {
+ my $name = $u->{name};
+
+ foreach my $range (@{$u->{subUidRanges}}) {
+ my $value = join(":", ($name, $range->{startUid}, $range->{count}));
+ push @subUids, $value;
+ }
+
+ foreach my $range (@{$u->{subGidRanges}}) {
+ my $value = join(":", ($name, $range->{startGid}, $range->{count}));
+ push @subGids, $value;
+ }
+
+ if($u->{isNormalUser}) {
+ my $subordinate = allocSubUid($name);
+ $subUidMap->{$name} = $subordinate;
+ my $value = join(":", ($name, $subordinate, 65536));
+ push @subUids, $value;
+ push @subGids, $value;
+ }
+}
+
+updateFile("/etc/subuid", join("\n", @subUids) . "\n");
+updateFile("/etc/subgid", join("\n", @subGids) . "\n");
+updateFile($subUidMapFile, encode_json($subUidMap) . "\n");
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 7e5f3582ae8..ee64f785f5b 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -375,18 +375,6 @@ let
};
};
- mkSubuidEntry = user: concatStrings (
- map (range: "${user.name}:${toString range.startUid}:${toString range.count}\n")
- user.subUidRanges);
-
- subuidFile = concatStrings (map mkSubuidEntry (attrValues cfg.users));
-
- mkSubgidEntry = user: concatStrings (
- map (range: "${user.name}:${toString range.startGid}:${toString range.count}\n")
- user.subGidRanges);
-
- subgidFile = concatStrings (map mkSubgidEntry (attrValues cfg.users));
-
idsAreUnique = set: idAttr: !(fold (name: args@{ dup, acc }:
let
id = builtins.toString (builtins.getAttr idAttr (builtins.getAttr name set));
@@ -406,6 +394,7 @@ let
{ inherit (u)
name uid group description home createHome isSystemUser
password passwordFile hashedPassword
+ isNormalUser subUidRanges subGidRanges
initialPassword initialHashedPassword;
shell = utils.toShellPath u.shell;
}) cfg.users;
@@ -567,16 +556,7 @@ in {
# Install all the user shells
environment.systemPackages = systemShells;
- environment.etc = {
- subuid = {
- text = subuidFile;
- mode = "0644";
- };
- subgid = {
- text = subgidFile;
- mode = "0644";
- };
- } // (mapAttrs' (name: { packages, ... }: {
+ environment.etc = (mapAttrs' (name: { packages, ... }: {
name = "profiles/per-user/${name}";
value.source = pkgs.buildEnv {
name = "user-environment";
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 105f0f2710e..55546ebfda8 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -23,6 +23,15 @@ in
maintainers = [] ++ lib.teams.podman.members;
};
+
+ imports = [
+ (
+ lib.mkRemovedOptionModule
+ [ "virtualisation" "containers" "users" ]
+ "All users with `isNormaUser = true` set now get appropriate subuid/subgid mappings."
+ )
+ ];
+
options.virtualisation.containers = {
enable =
@@ -99,15 +108,6 @@ in
'';
};
- users = mkOption {
- default = [];
- type = types.listOf types.str;
- description = ''
- List of users to set up subuid/subgid mappings for.
- This is a requirement for running rootless containers.
- '';
- };
-
};
config = lib.mkIf cfg.enable {
@@ -122,26 +122,6 @@ in
registries = lib.mapAttrs (n: v: { registries = v; }) cfg.registries;
};
- users.extraUsers = builtins.listToAttrs (
- (
- builtins.foldl' (
- acc: user: {
- values = acc.values ++ [
- {
- name = user;
- value = {
- subUidRanges = [ { startUid = acc.offset; count = 65536; } ];
- subGidRanges = [ { startGid = acc.offset; count = 65536; } ];
- };
- }
- ];
- offset = acc.offset + 65536;
- }
- )
- { values = []; offset = 100000; } (lib.unique cfg.users)
- ).values
- );
-
environment.etc."containers/policy.json".source =
if cfg.policy != {} then pkgs.writeText "policy.json" (builtins.toJSON cfg.policy)
else copyFile "${pkgs.skopeo.src}/default-policy.json";
diff --git a/nixos/tests/podman.nix b/nixos/tests/podman.nix
index 7a83689a49a..aea1516c4f7 100644
--- a/nixos/tests/podman.nix
+++ b/nixos/tests/podman.nix
@@ -12,9 +12,6 @@ import ./make-test-python.nix (
{ pkgs, ... }:
{
virtualisation.podman.enable = true;
- virtualisation.containers.users = [
- "alice"
- ];
users.users.alice = {
isNormalUser = true;