From 5733967290f19f5e1ee43ef057b9b31c647ba80d Mon Sep 17 00:00:00 2001 From: adisbladis Date: Tue, 28 Apr 2020 22:30:34 +0100 Subject: [PATCH] nixos.users-groups: Set up subuid/subgid mappings for all normal users This is required by (among others) Podman to run containers in rootless mode. Other distributions such as Fedora and Ubuntu already set up these mappings. The scheme with a start UID/GID offset starting at 100000 and increasing in 65536 increments is copied from Fedora. --- nixos/modules/config/update-users-groups.pl | 55 +++++++++++++++++++++ nixos/modules/config/users-groups.nix | 24 +-------- nixos/modules/virtualisation/containers.nix | 38 ++++---------- nixos/tests/podman.nix | 3 -- 4 files changed, 66 insertions(+), 54 deletions(-) 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;